Parcourir la source

chore(cli): Start work on migrate command

Michael Bromley il y a 1 an
Parent
commit
68670d14e9

+ 19 - 19
package-lock.json

@@ -3630,6 +3630,18 @@
                 "sisteransi": "^1.0.5"
             }
         },
+        "node_modules/@clack/prompts/node_modules/is-unicode-supported": {
+            "version": "1.3.0",
+            "extraneous": true,
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=12"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
         "node_modules/@clr/angular": {
             "version": "17.0.1",
             "license": "MIT",
@@ -3952,7 +3964,6 @@
         },
         "node_modules/@cspotcode/source-map-support": {
             "version": "0.8.1",
-            "devOptional": true,
             "license": "MIT",
             "dependencies": {
                 "@jridgewell/trace-mapping": "0.3.9"
@@ -3963,7 +3974,6 @@
         },
         "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": {
             "version": "0.3.9",
-            "devOptional": true,
             "license": "MIT",
             "dependencies": {
                 "@jridgewell/resolve-uri": "^3.0.3",
@@ -9381,7 +9391,7 @@
         },
         "node_modules/@swc/core": {
             "version": "1.4.7",
-            "dev": true,
+            "devOptional": true,
             "hasInstallScript": true,
             "license": "Apache-2.0",
             "dependencies": {
@@ -9448,12 +9458,12 @@
         },
         "node_modules/@swc/counter": {
             "version": "0.1.3",
-            "dev": true,
+            "devOptional": true,
             "license": "Apache-2.0"
         },
         "node_modules/@swc/types": {
             "version": "0.1.5",
-            "dev": true,
+            "devOptional": true,
             "license": "Apache-2.0"
         },
         "node_modules/@tokenizer/token": {
@@ -9513,22 +9523,18 @@
         },
         "node_modules/@tsconfig/node10": {
             "version": "1.0.9",
-            "devOptional": true,
             "license": "MIT"
         },
         "node_modules/@tsconfig/node12": {
             "version": "1.0.11",
-            "devOptional": true,
             "license": "MIT"
         },
         "node_modules/@tsconfig/node14": {
             "version": "1.0.3",
-            "devOptional": true,
             "license": "MIT"
         },
         "node_modules/@tsconfig/node16": {
             "version": "1.0.4",
-            "devOptional": true,
             "license": "MIT"
         },
         "node_modules/@tufjs/canonical-json": {
@@ -11386,7 +11392,6 @@
         },
         "node_modules/acorn-walk": {
             "version": "8.3.2",
-            "devOptional": true,
             "license": "MIT",
             "engines": {
                 "node": ">=0.4.0"
@@ -11678,7 +11683,6 @@
         },
         "node_modules/arg": {
             "version": "4.1.3",
-            "devOptional": true,
             "license": "MIT"
         },
         "node_modules/argparse": {
@@ -14220,7 +14224,6 @@
         },
         "node_modules/create-require": {
             "version": "1.1.1",
-            "devOptional": true,
             "license": "MIT"
         },
         "node_modules/crelt": {
@@ -14869,7 +14872,6 @@
         },
         "node_modules/diff": {
             "version": "4.0.2",
-            "devOptional": true,
             "license": "BSD-3-Clause",
             "engines": {
                 "node": ">=0.3.1"
@@ -21967,7 +21969,6 @@
         },
         "node_modules/make-error": {
             "version": "1.3.6",
-            "devOptional": true,
             "license": "ISC"
         },
         "node_modules/make-fetch-happen": {
@@ -29549,8 +29550,8 @@
         },
         "node_modules/ts-node": {
             "version": "10.9.2",
-            "devOptional": true,
-            "license": "MIT",
+            "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
+            "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
             "dependencies": {
                 "@cspotcode/source-map-support": "^0.8.0",
                 "@tsconfig/node10": "^1.0.7",
@@ -30587,7 +30588,6 @@
         },
         "node_modules/v8-compile-cache-lib": {
             "version": "3.0.1",
-            "devOptional": true,
             "license": "MIT"
         },
         "node_modules/valid-data-url": {
@@ -31710,7 +31710,6 @@
         },
         "node_modules/yn": {
             "version": "3.1.1",
-            "devOptional": true,
             "license": "MIT",
             "engines": {
                 "node": ">=6"
@@ -31917,7 +31916,8 @@
                 "commander": "^11.0.0",
                 "fs-extra": "^11.2.0",
                 "picocolors": "^1.0.0",
-                "ts-morph": "^21.0.1"
+                "ts-morph": "^21.0.1",
+                "ts-node": "^10.9.2"
             },
             "bin": {
                 "vendure": "dist/cli.js"

+ 2 - 1
packages/cli/package.json

@@ -40,7 +40,8 @@
         "commander": "^11.0.0",
         "fs-extra": "^11.2.0",
         "picocolors": "^1.0.0",
-        "ts-morph": "^21.0.1"
+        "ts-morph": "^21.0.1",
+        "ts-node": "^10.9.2"
     },
     "devDependencies": {
         "typescript": "5.3.3"

+ 69 - 0
packages/cli/src/commands/migrate/generate-migration/generate-migration.ts

@@ -0,0 +1,69 @@
+import { cancel, isCancel, log, text } from '@clack/prompts';
+import { generateMigration } from '@vendure/core';
+import path from 'node:path';
+import { register } from 'ts-node';
+
+import { CliCommand, CliCommandReturnVal } from '../../../shared/cli-command';
+import { analyzeProject } from '../../../shared/shared-prompts';
+import { VendureConfigRef } from '../../../shared/vendure-config-ref';
+import { VendurePluginRef } from '../../../shared/vendure-plugin-ref';
+import { isRunningInTsNode } from '../../../utilities/utils';
+
+const cancelledMessage = 'Add entity cancelled';
+
+export interface GenerateMigrationOptions {
+    plugin?: VendurePluginRef;
+}
+
+export const generateMigrationCommand = new CliCommand({
+    id: 'generate-migration',
+    category: 'Other',
+    description: 'Generate a new database migration',
+    run: options => runGenerateMigration(options),
+});
+
+async function runGenerateMigration(
+    options?: Partial<GenerateMigrationOptions>,
+): Promise<CliCommandReturnVal> {
+    const project = await analyzeProject({ cancelledMessage });
+    const vendureConfig = new VendureConfigRef(project);
+    log.info('Using VendureConfig from ' + vendureConfig.getPathRelativeToProjectRoot());
+
+    const name = await text({
+        message: 'Enter a meaningful name for the migration',
+        initialValue: '',
+        placeholder: 'add-custom-fields',
+        validate: input => {
+            if (!/^[a-zA-Z][a-zA-Z-_0-9]+$/.test(input)) {
+                return 'The plugin name must contain only letters, numbers, underscores and dashes';
+            }
+        },
+    });
+    if (isCancel(name)) {
+        cancel(cancelledMessage);
+        process.exit(0);
+    }
+    const config = loadVendureConfigFile(vendureConfig);
+    await generateMigration(config, { name, outputDir: './src/migrations' });
+
+    return {
+        project,
+        modifiedSourceFiles: [],
+    };
+}
+
+function loadVendureConfigFile(vendureConfig: VendureConfigRef) {
+    if (!isRunningInTsNode()) {
+        const tsConfigPath = path.join(process.cwd(), 'tsconfig.json');
+        // eslint-disable-next-line @typescript-eslint/no-var-requires
+        const compilerOptions = require(tsConfigPath).compilerOptions;
+        register({ compilerOptions });
+    }
+    const exportedVarName = vendureConfig.getConfigObjectVariableName();
+    if (!exportedVarName) {
+        throw new Error('Could not find the exported variable name in the VendureConfig file');
+    }
+    // eslint-disable-next-line @typescript-eslint/no-var-requires
+    const config = require(vendureConfig.sourceFile.getFilePath())[exportedVarName];
+    return config;
+}

+ 47 - 0
packages/cli/src/commands/migrate/migrate.ts

@@ -0,0 +1,47 @@
+import { cancel, intro, isCancel, log, outro, select } from '@clack/prompts';
+import { Command } from 'commander';
+import pc from 'picocolors';
+
+import { generateMigrationCommand } from './generate-migration/generate-migration';
+
+const cancelledMessage = 'Migrate cancelled.';
+
+/**
+ * This command is currently not exposed due to unresolved issues which I think are related to
+ * peculiarities in loading ESM modules vs CommonJS modules. More time is needed to dig into
+ * this before we expose this command in the cli.ts file.
+ */
+export function registerMigrateCommand(program: Command) {
+    program
+        .command('migrate')
+        .description('Generate, run or revert a database migration')
+        .action(async () => {
+            // eslint-disable-next-line no-console
+            console.log(`\n`);
+            intro(pc.blue('➡️ Vendure migrations'));
+            const action = await select({
+                message: 'What would you like to do?',
+                options: [
+                    { value: 'generate', label: 'Generate a new migration' },
+                    { value: 'run', label: 'Run pending migrations' },
+                    { value: 'revert', label: 'Revert the last migration' },
+                ],
+            });
+            if (isCancel(action)) {
+                cancel(cancelledMessage);
+                process.exit(0);
+            }
+            try {
+                if (action === 'generate') {
+                    await generateMigrationCommand.run();
+                }
+                outro('✅ Done!');
+            } catch (e: any) {
+                log.error(e.message as string);
+                if (e.stack) {
+                    log.error(e.stack);
+                }
+            }
+            process.exit(0);
+        });
+}

+ 5 - 0
packages/cli/src/utilities/utils.ts

@@ -5,3 +5,8 @@
 export async function pauseForPromptDisplay() {
     await new Promise(resolve => setTimeout(resolve, 100));
 }
+
+export function isRunningInTsNode(): boolean {
+    // @ts-ignore
+    return process[Symbol.for('ts-node.register.instance')] != null;
+}