Browse Source

feat(server): Create a CLI program to simplify starting a new project

Relates to #44
Michael Bromley 7 years ago
parent
commit
24ae1342c6

+ 9 - 1
server/build/gulpfile.ts

@@ -5,4 +5,12 @@ gulp.task('copy-schemas', () => {
     return gulp.src(['../src/**/*.graphql']).pipe(gulp.dest('../dist/server/src'));
 });
 
-gulp.task('default', gulp.series('copy-schemas'));
+gulp.task('copy-email-templates', () => {
+    return gulp.src(['../src/email/templates/**/*']).pipe(gulp.dest('../dist/cli/assets/email-templates'));
+});
+
+gulp.task('copy-cli-assets', () => {
+    return gulp.src(['../cli/assets/**/*']).pipe(gulp.dest('../dist/cli/assets'));
+});
+
+gulp.task('default', gulp.parallel('copy-schemas', 'copy-email-templates', 'copy-cli-assets'));

+ 10 - 0
server/build/tsconfig.cli.json

@@ -0,0 +1,10 @@
+{
+  "extends": "../tsconfig.json",
+  "compilerOptions": {
+    "outDir": "../dist/cli",
+    "declaration": false
+  },
+  "files": [
+    "../cli/vendure-cli.ts"
+  ]
+}

+ 66 - 0
server/cli/assets/index.hbs

@@ -0,0 +1,66 @@
+{{#if isTs }}import{{ else }}const{{/if}} {
+    bootstrap,
+    fakePalPaymentHandler,
+    gripePaymentHandler,
+    defaultEmailTypes,
+    HandlebarsMjmlGenerator,
+    DefaultAssetServerPlugin,
+} {{#if isTs}}from 'vendure'; {{ else }}= require('vendure');{{/if}}
+{{#if isTs }}
+import * as path from 'path';
+{{ else }}
+const path = require('path');
+{{/if}}
+
+bootstrap({
+    defaultChannelToken: 'default-channel',
+    authOptions: {
+        sessionSecret: '{{ sessionSecret }}',
+    },
+    port: 3000,
+    apiPath: 'api',
+    dbConnectionOptions: {
+        type: '{{ dbType }}',
+        synchronize: true, // turn this off for production
+        logging: false,
+        host: '{{ dbHost }}',
+        port: 3306,
+        username: '{{ dbUserName }}',
+        password: '{{ dbPassword }}',
+        database: '{{ dbName }}',
+    },
+    paymentOptions: {
+        paymentMethodHandlers: [fakePalPaymentHandler, gripePaymentHandler],
+    },
+    customFields: {},
+    emailOptions: {
+        emailTypes: defaultEmailTypes,
+        generator: new HandlebarsMjmlGenerator(path.join(__dirname, 'vendure', 'email', 'templates', 'partials')),
+        transport: {
+            type: 'file',
+            outputPath: path.join(__dirname, 'vendure', 'email', 'test-emails'),
+        },
+    },
+    importExportOptions: {
+        importAssetsDir: path.join(__dirname, 'vendure', 'import-assets'),
+    },
+    plugins: [
+        new DefaultAssetServerPlugin({
+            route: 'assets',
+            assetUploadDir: path.join(__dirname, 'vendure', 'assets'),
+            port: 4000,
+            hostname: 'http://localhost',
+            previewMaxHeight: 1600,
+            previewMaxWidth: 1600,
+            presets: [
+                { name: 'tiny', width: 50, height: 50, mode: 'crop' },
+                { name: 'thumb', width: 150, height: 150, mode: 'crop' },
+                { name: 'small', width: 300, height: 300, mode: 'resize' },
+                { name: 'medium', width: 500, height: 500, mode: 'resize' },
+                { name: 'large', width: 800, height: 800, mode: 'resize' },
+            ],
+        }),
+    ],
+}).catch(err => {
+    console.log(err);
+});

+ 119 - 0
server/cli/vendure-cli.ts

@@ -0,0 +1,119 @@
+#!/usr/bin/env node
+
+import * as fs from 'fs-extra';
+import * as Handlebars from 'handlebars';
+import * as path from 'path';
+import * as prompts from 'prompts';
+
+// tslint:disable:no-console
+console.log(
+    '\x1b[36m%s\x1b[0m',
+    `
+                      _
+                     | |
+ __   _____ _ __   __| |_   _ _ __ ___
+ \\ \\ / / _ \\ '_ \\ / _\` | | | | '__/ _ \\
+  \\ V /  __/ | | | (_| | |_| | | |  __/
+   \\_/ \\___|_| |_|\\__,_|\\__,_|_|  \\___|
+
+
+                                       `,
+);
+
+console.log(`Let's get started with a new Vendure server!\n`);
+prompts([
+    {
+        type: 'select',
+        name: 'dbType',
+        message: 'Which database are you using?',
+        choices: [
+            { title: 'MySQL / MariaDB', value: 'mysql' },
+            { title: 'Postgres', value: 'postgres' },
+            { title: 'SQLite', value: 'sqlite' },
+            { title: 'MS SQL Server', value: 'mssql' },
+            { title: 'Oracle', value: 'oracle' },
+        ],
+        initial: 0 as any,
+    },
+    {
+        type: 'text',
+        name: 'dbHost',
+        message: `What's the database host address?`,
+        initial: '192.168.99.100',
+    },
+    {
+        type: 'text',
+        name: 'dbName',
+        message: `What's the name of the database?`,
+        initial: 'vendure',
+    },
+    {
+        type: 'text',
+        name: 'dbUserName',
+        message: `What's the database user name?`,
+        initial: 'root',
+    },
+    {
+        type: 'password',
+        name: 'dbPassword',
+        message: `What's the database password?`,
+    },
+    {
+        type: 'select',
+        name: 'language',
+        message: 'Which language will you be using?',
+        choices: [{ title: 'TypeScript', value: 'ts' }, { title: 'JavaScript', value: 'js' }],
+        initial: 0 as any,
+    },
+]).then(
+    async answers => {
+        if (!answers.language) {
+            console.log('Setup aborted. No changes made');
+            process.exit(0);
+        }
+        await createDirectoryStructure();
+        await copyEmailTemplates();
+        await createIndexFile(answers);
+        console.log(
+            '\x1b[36m%s\x1b[0m',
+            `\nAll done! You can now execute the index.${answers.language} file to start the server.`,
+        );
+    },
+    err => console.log('error', err),
+);
+
+/**
+ * Generate the default directory structure for a new Vendure project
+ */
+async function createDirectoryStructure() {
+    const cwd = process.cwd();
+    await fs.ensureDir(path.join(cwd, 'vendure', 'email', 'test-emails'));
+    await fs.ensureDir(path.join(cwd, 'vendure', 'import-assets'));
+    await fs.ensureDir(path.join(cwd, 'vendure', 'assets'));
+}
+
+/**
+ * Copy the email templates into the app
+ */
+async function copyEmailTemplates() {
+    const templateDir = path.join(__dirname, 'assets', 'email-templates');
+    await fs.copy(templateDir, path.join(process.cwd(), 'vendure', 'email', 'templates'));
+}
+
+/**
+ * Create the server index file based on the options specified by the CLI prompts.
+ */
+async function createIndexFile(answers: any) {
+    const cwd = process.cwd();
+
+    const templateContext = {
+        ...answers,
+        isTs: answers.language === 'ts',
+        sessionSecret: Math.random()
+            .toString(36)
+            .substr(3),
+    };
+    const template = await fs.readFile(path.join(__dirname, 'assets', 'index.hbs'), 'utf-8');
+    const indexSource = Handlebars.compile(template)(templateContext);
+    await fs.writeFile(path.join(cwd, 'index.' + answers.language), indexSource);
+}

+ 4 - 1
server/package.json

@@ -13,10 +13,11 @@
     "test:cov": "jest --coverage",
     "test:e2e": "jest --config ./e2e/config/jest-e2e.json --runInBand",
     "test:e2e:watch": "jest --config ./e2e/config/jest-e2e.json --watch --runInBand",
-    "build": "rimraf dist && tsc -p ./build/tsconfig.build.json && gulp -f ./build/gulpfile.ts",
+    "build": "rimraf dist && tsc -p ./build/tsconfig.build.json && tsc -p ./build/tsconfig.cli.json && gulp -f ./build/gulpfile.ts",
     "generate-email-preview": "node -r ts-node/register src/email/preview/generate-email-preview.ts"
   },
   "main": "dist/server/src/index.js",
+  "bin": "dist/cli/vendure-cli.js",
   "files": [
     "dist/**/*"
   ],
@@ -51,6 +52,7 @@
     "mysql": "^2.16.0",
     "nanoid": "^2.0.0",
     "nodemailer": "^4.7.0",
+    "prompts": "^1.2.1",
     "reflect-metadata": "^0.1.12",
     "rxjs": "^6.3.3",
     "sharp": "^0.21.0",
@@ -74,6 +76,7 @@
     "@types/nanoid": "^1.2.0",
     "@types/node": "^10.12.9",
     "@types/nodemailer": "^4.6.5",
+    "@types/prompts": "^1.1.1",
     "@types/sharp": "^0.21.0",
     "faker": "^4.1.0",
     "graphql-request": "^1.8.2",

+ 1 - 1
server/src/config/email/email-transport-options.ts

@@ -49,7 +49,7 @@ export interface FileTransportOptions {
     /** The directory in which the emails will be saved */
     outputPath: string;
     /** When set to true, a raw text file will be output rather than an HTML file */
-    raw: boolean;
+    raw?: boolean;
 }
 
 /**

+ 23 - 0
server/yarn.lock

@@ -347,6 +347,11 @@
     "@types/events" "*"
     "@types/node" "*"
 
+"@types/prompts@^1.1.1":
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/@types/prompts/-/prompts-1.1.1.tgz#5688abfa6a95ddfe808f8ae5337e86e5cf8f3147"
+  integrity sha512-iqE02VXr48Dw//ZBTU0wiL1gbBn3sclnAuTztMLjd2L3bKgq2Qcn0d6ezIXFMaaAl2382g99CUp/p3scU/JCpQ==
+
 "@types/range-parser@*":
   version "1.2.2"
   resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.2.tgz#fa8e1ad1d474688a757140c91de6dace6f4abc8d"
@@ -4014,6 +4019,11 @@ kind-of@^6.0.0, kind-of@^6.0.2:
   version "6.0.2"
   resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051"
 
+kleur@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.0.tgz#ae185c2e0641ffb2f8e3d182df13239df9d8d3b2"
+  integrity sha512-acHc4xKlqTNuAGmbvtd3KuNi1bnlHsdPg6Os1P5s/Ii/6g8MY3caVPDp4md04/twbh4wwPvdpnol1bc9zFsI3w==
+
 last-run@^1.1.0:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/last-run/-/last-run-1.1.1.tgz#45b96942c17b1c79c772198259ba943bebf8ca5b"
@@ -5484,6 +5494,14 @@ prompts@^0.1.9:
     clorox "^1.0.3"
     sisteransi "^0.1.1"
 
+prompts@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/prompts/-/prompts-1.2.1.tgz#7fd4116a458d6a62761e3ccb1432d7bbd8b2cb29"
+  integrity sha512-GE33SMMVO1ISfnq3i6cE+WYK/tLxRWtZiRkl5vdg0KR0owOCPFOsq8BuFajFbW7b2bMHb8krXaQHOpZyUEuvmA==
+  dependencies:
+    kleur "^3.0.0"
+    sisteransi "^1.0.0"
+
 proto-list@~1.2.1:
   version "1.2.4"
   resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849"
@@ -6124,6 +6142,11 @@ sisteransi@^0.1.1:
   version "0.1.1"
   resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-0.1.1.tgz#5431447d5f7d1675aac667ccd0b865a4994cb3ce"
 
+sisteransi@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.0.tgz#77d9622ff909080f1c19e5f4a1df0c1b0a27b88c"
+  integrity sha512-N+z4pHB4AmUv0SjveWRd6q1Nj5w62m5jodv+GD8lvmbY/83T/rpbJGZOnK5T149OldDj4Db07BSv9xY4K6NTPQ==
+
 slash@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"