Răsfoiți Sursa

feat(create): Rework folder structure, add build & migration scripts

Closes #175
Michael Bromley 6 ani în urmă
părinte
comite
746abff9d3

+ 32 - 15
packages/create/src/create-vendure-app.ts

@@ -69,9 +69,15 @@ async function createApp(
 
     const root = path.resolve(name);
     const appName = path.basename(root);
-    const { dbType, usingTs, configSource, indexSource, indexWorkerSource, populateProducts } = isCi
-        ? await gatherCiUserResponses(root)
-        : await gatherUserResponses(root);
+    const {
+        dbType,
+        usingTs,
+        configSource,
+        indexSource,
+        indexWorkerSource,
+        migrationSource,
+        populateProducts,
+    } = isCi ? await gatherCiUserResponses(root) : await gatherUserResponses(root);
 
     const useYarn = useNpm ? false : shouldUseYarn();
     const originalDirectory = process.cwd();
@@ -85,9 +91,13 @@ async function createApp(
         version: '0.1.0',
         private: true,
         scripts: {
-            'run:server': usingTs ? 'ts-node index.ts' : 'node index.js',
-            'run:worker': usingTs ? 'ts-node index-worker.ts' : 'node index-worker.js',
+            'run:server': usingTs ? 'ts-node ./src/index.ts' : 'node ./src/index.js',
+            'run:worker': usingTs ? 'ts-node ./src/index-worker.ts' : 'node ./src/index-worker.js',
             start: useYarn ? 'concurrently yarn:run:*' : 'concurrently npm:run:*',
+            ...(usingTs ? { build: 'tsc' } : undefined),
+            'migration:generate': usingTs ? 'ts-node migration generate' : 'node migration generate',
+            'migration:run': usingTs ? 'ts-node migration run' : 'node migration run',
+            'migration:revert': usingTs ? 'ts-node migration revert' : 'node migration revert',
         },
     };
 
@@ -129,22 +139,29 @@ async function createApp(
             title: 'Generating app scaffold',
             task: ctx => {
                 return new Observable(subscriber => {
+                    fs.ensureDirSync(path.join(root, 'src'));
                     const assetPath = (fileName: string) => path.join(__dirname, '../assets', fileName);
+                    const srcPathScript = (fileName: string): string =>
+                        path.join(root, 'src', `${fileName}.${usingTs ? 'ts' : 'js'}`);
                     const rootPathScript = (fileName: string): string =>
                         path.join(root, `${fileName}.${usingTs ? 'ts' : 'js'}`);
-                    ctx.configFile = rootPathScript('vendure-config');
+                    ctx.configFile = srcPathScript('vendure-config');
 
                     fs.writeFile(ctx.configFile, configSource)
                         .then(() => {
-                            subscriber.next(`Created config`);
-                            return fs.writeFile(rootPathScript('index'), indexSource);
+                            subscriber.next(`Created config file`);
+                            return fs.writeFile(srcPathScript('index'), indexSource);
                         })
                         .then(() => {
-                            subscriber.next(`Created index`);
-                            return fs.writeFile(rootPathScript('index-worker'), indexWorkerSource);
+                            subscriber.next(`Created index file`);
+                            return fs.writeFile(srcPathScript('index-worker'), indexWorkerSource);
                         })
                         .then(() => {
-                            subscriber.next(`Created worker`);
+                            subscriber.next(`Created worker file`);
+                            return fs.writeFile(rootPathScript('migration'), migrationSource);
+                        })
+                        .then(() => {
+                            subscriber.next(`Created migration file`);
                             if (usingTs) {
                                 return fs.copyFile(
                                     assetPath('tsconfig.template.json'),
@@ -274,9 +291,9 @@ function runPreChecks(name: string | undefined, useNpm: boolean): name is string
  * Generate the default directory structure for a new Vendure project
  */
 async function createDirectoryStructure(root: string) {
-    await fs.ensureDir(path.join(root, 'vendure', 'email', 'test-emails'));
-    await fs.ensureDir(path.join(root, 'vendure', 'import-assets'));
-    await fs.ensureDir(path.join(root, 'vendure', 'assets'));
+    await fs.ensureDir(path.join(root, 'static', 'email', 'test-emails'));
+    await fs.ensureDir(path.join(root, 'static', 'import-assets'));
+    await fs.ensureDir(path.join(root, 'static', 'assets'));
 }
 
 /**
@@ -285,7 +302,7 @@ async function createDirectoryStructure(root: string) {
 async function copyEmailTemplates(root: string) {
     const templateDir = path.join(root, 'node_modules/@vendure/email-plugin/templates');
     try {
-        await fs.copy(templateDir, path.join(root, 'vendure', 'email', 'templates'));
+        await fs.copy(templateDir, path.join(root, 'static', 'email', 'templates'));
     } catch (err) {
         console.error(chalk.red(`Failed to copy email templates.`));
     }

+ 19 - 4
packages/create/src/gather-user-responses.ts

@@ -94,11 +94,15 @@ export async function gatherUserResponses(root: string): Promise<UserResponses>
         process.exit(0);
     }
 
-    const { indexSource, indexWorkerSource, configSource } = await generateSources(root, answers);
+    const { indexSource, indexWorkerSource, configSource, migrationSource } = await generateSources(
+        root,
+        answers,
+    );
     return {
         indexSource,
         indexWorkerSource,
         configSource,
+        migrationSource,
         usingTs: answers.language === 'ts',
         dbType: answers.dbType,
         populateProducts: answers.populateProducts,
@@ -119,11 +123,15 @@ export async function gatherCiUserResponses(root: string): Promise<UserResponses
         language: 'ts',
         populateProducts: true,
     };
-    const { indexSource, indexWorkerSource, configSource } = await generateSources(root, ciAnswers);
+    const { indexSource, indexWorkerSource, configSource, migrationSource } = await generateSources(
+        root,
+        ciAnswers,
+    );
     return {
         indexSource,
         indexWorkerSource,
         configSource,
+        migrationSource,
         usingTs: ciAnswers.language === 'ts',
         dbType: ciAnswers.dbType,
         populateProducts: ciAnswers.populateProducts,
@@ -136,7 +144,12 @@ export async function gatherCiUserResponses(root: string): Promise<UserResponses
 async function generateSources(
     root: string,
     answers: any,
-): Promise<{ indexSource: string; indexWorkerSource: string; configSource: string }> {
+): Promise<{
+    indexSource: string;
+    indexWorkerSource: string;
+    configSource: string;
+    migrationSource: string;
+}> {
     const assetPath = (fileName: string) => path.join(__dirname, '../assets', fileName);
 
     const templateContext = {
@@ -155,7 +168,9 @@ async function generateSources(
     const indexSource = Handlebars.compile(indexTemplate)(templateContext);
     const indexWorkerTemplate = await fs.readFile(assetPath('index-worker.hbs'), 'utf-8');
     const indexWorkerSource = Handlebars.compile(indexWorkerTemplate)(templateContext);
-    return { indexSource, indexWorkerSource, configSource };
+    const migrationTemplate = await fs.readFile(assetPath('migration.hbs'), 'utf-8');
+    const migrationSource = Handlebars.compile(migrationTemplate)(templateContext);
+    return { indexSource, indexWorkerSource, configSource, migrationSource };
 }
 
 function defaultDBPort(dbType: DbType): number {

+ 1 - 0
packages/create/src/types.ts

@@ -7,6 +7,7 @@ export interface UserResponses {
     indexSource: string;
     indexWorkerSource: string;
     configSource: string;
+    migrationSource: string;
 }
 
 export type CliLogLevel = 'silent' | 'info' | 'verbose';

+ 27 - 0
packages/create/templates/migration.hbs

@@ -0,0 +1,27 @@
+{{#if isTs }}import { generateMigration, revertLastMigration, runMigrations } from '@vendure/core';{{else}}const { generateMigration, revertLastMigration, runMigrations } = require('@vendure/core');{{/if}}
+{{#if isTs }}import program from 'commander';{{else}}const program = require('commander');{{/if}}
+
+{{#if isTs }}import { config } from './src/vendure-config';{{else}}const { config } = require('./src/vendure-config');{{/if}}
+
+program
+    .command('generate <name>')
+    .description('Generate a new migration file with the given name')
+    .action(name => {
+        return generateMigration(config, { name, outputDir: './migrations' });
+    });
+
+program
+    .command('run')
+    .description('Run all pending migrations')
+    .action(() => {
+        return runMigrations(config);
+    });
+
+program
+    .command('revert')
+    .description('Revert the last applied migration')
+    .action(() => {
+        return revertLastMigration(config);
+    });
+
+program.parse(process.argv);

+ 1 - 1
packages/create/templates/tsconfig.template.json

@@ -11,5 +11,5 @@
     "outDir": "./dist",
     "baseUrl": "./"
   },
-  "exclude": ["node_modules"]
+  "exclude": ["node_modules", "migration.ts"]
 }

+ 5 - 5
packages/create/templates/vendure-config.hbs

@@ -39,7 +39,7 @@ const path = require('path');
         synchronize: false, // not working with SQLite/SQL.js, see https://github.com/typeorm/typeorm/issues/2576
         {{/if}}
         logging: false,
-        database: {{#if isSQLjs}}new Uint8Array([]){{else if isSQLite}}path.join(__dirname, 'vendure.sqlite'){{else}}'{{ dbName }}'{{/if}},
+        database: {{#if isSQLjs}}new Uint8Array([]){{else if isSQLite}}path.join(__dirname, '../vendure.sqlite'){{else}}'{{ dbName }}'{{/if}},
         {{#if isSQLjs}}
         location: path.join(__dirname, 'vendure.sqlite'),
         autoSave: true,
@@ -50,7 +50,7 @@ const path = require('path');
         username: '{{ dbUserName }}',
         password: '{{ dbPassword }}',
         {{/if}}
-        migrations: [path.join(__dirname, 'migrations/*.ts')],
+        migrations: [path.join(__dirname, '../migrations/*.ts')],
     },
     paymentOptions: {
         paymentMethodHandlers: [examplePaymentHandler],
@@ -59,16 +59,16 @@ const path = require('path');
     plugins: [
         AssetServerPlugin.init({
             route: 'assets',
-            assetUploadDir: path.join(__dirname, 'vendure/assets'),
+            assetUploadDir: path.join(__dirname, '../static/assets'),
             port: 3001,
         }),
         DefaultSearchPlugin,
         EmailPlugin.init({
             devMode: true,
-            outputPath: path.join(__dirname, 'vendure/email/test-emails'),
+            outputPath: path.join(__dirname, '../static/email/test-emails'),
             mailboxPort: 3003,
             handlers: defaultEmailHandlers,
-            templatePath: path.join(__dirname, 'vendure/email/templates'),
+            templatePath: path.join(__dirname, '../static/email/templates'),
             globalTemplateVars: {
                 // The following variables will change depending on your storefront implementation
                 fromAddress: '"example" <noreply@example.com>',