Procházet zdrojové kódy

refactor(cli): Refactor cli to use declarative array approach

HouseinIsProgramming před 7 měsíci
rodič
revize
92f71ea1fa

+ 113 - 0
packages/cli/src/README.md

@@ -0,0 +1,113 @@
+# Vendure CLI Command Structure
+
+This document describes the new array-based CLI command structure that allows for easy declaration and management of CLI commands.
+
+## Overview
+
+The CLI now uses a structured approach where all commands are defined in an array of `CliCommandDefinition` objects, making it easy to add, remove, and modify commands.
+
+## Command Definition Interface
+
+```typescript
+interface CliCommandDefinition {
+    name: string;                    // The command name (e.g., 'add', 'migrate')
+    description: string;             // Command description shown in help
+    options?: CliCommandOption[];    // Optional array of command options
+    action: (options?: Record<string, any>) => Promise<void>; // Command implementation
+}
+```
+
+## Option Definition Interface
+
+```typescript
+interface CliCommandOption {
+    flag: string;                    // Option flag (e.g., '-f, --file <path>')
+    description: string;             // Option description
+    required?: boolean;              // Whether the option is required
+    defaultValue?: any;              // Default value for the option
+}
+```
+
+## Adding New Commands
+
+To add a new command, simply add it to the `cliCommands` array in `packages/cli/src/commands/command-declarations.ts`:
+
+```typescript
+export const cliCommands: CliCommandDefinition[] = [
+    // ... existing commands ...
+    {
+        name: 'new-command',
+        description: 'Description of the new command',
+        options: [
+            {
+                flag: '-o, --option <value>',
+                description: 'Description of the option',
+                required: true,
+            },
+        ],
+        action: async (options) => {
+            // Command implementation
+            console.log('Command executed with options:', options);
+            process.exit(0);
+        },
+    },
+];
+```
+
+## Examples
+
+### Simple Command (No Options)
+```typescript
+{
+    name: 'add',
+    description: 'Add a feature to your Vendure project',
+    action: async () => {
+        const { addCommand } = await import('./add/add');
+        await addCommand();
+        process.exit(0);
+    },
+}
+```
+
+### Command with Options
+```typescript
+{
+    name: 'example',
+    description: 'Example command with options',
+    options: [
+        {
+            flag: '-f, --file <path>',
+            description: 'Path to the file',
+            required: true,
+        },
+        {
+            flag: '-v, --verbose',
+            description: 'Enable verbose output',
+            required: false,
+            defaultValue: false,
+        },
+    ],
+    action: async (options) => {
+        if (options) {
+            console.log('File path:', options.file);
+            console.log('Verbose mode:', options.verbose);
+        }
+        process.exit(0);
+    },
+}
+```
+
+## Benefits
+
+1. **Centralized Management**: All commands are defined in one place
+2. **Type Safety**: Full TypeScript support with proper interfaces
+3. **Easy Extension**: Adding new commands is straightforward
+4. **Consistent Structure**: All commands follow the same pattern
+5. **Option Support**: Built-in support for command options with validation
+
+## File Structure
+
+- `packages/cli/src/shared/cli-command-definition.ts` - Interface definitions
+- `packages/cli/src/shared/command-registry.ts` - Command registration utility
+- `packages/cli/src/commands/command-declarations.ts` - Command declarations array
+- `packages/cli/src/cli.ts` - Main CLI entry point 

+ 5 - 17
packages/cli/src/cli.ts

@@ -3,6 +3,9 @@
 import { Command } from 'commander';
 import pc from 'picocolors';
 
+import { cliCommands } from './commands/command-declarations';
+import { registerCommands } from './shared/command-registry';
+
 const program = new Command();
 
 // eslint-disable-next-line @typescript-eslint/no-var-requires
@@ -24,22 +27,7 @@ Y88  88P 88888888 888  888 888  888 888  888 888    88888888
 `),
     );
 
-program
-    .command('add')
-    .description('Add a feature to your Vendure project')
-    .action(async () => {
-        const { addCommand } = await import('./commands/add/add');
-        await addCommand();
-        process.exit(0);
-    });
-
-program
-    .command('migrate')
-    .description('Generate, run or revert a database migration')
-    .action(async () => {
-        const { migrateCommand } = await import('./commands/migrate/migrate');
-        await migrateCommand();
-        process.exit(0);
-    });
+// Register all commands from the array
+registerCommands(program, cliCommands);
 
 void program.parseAsync(process.argv);

+ 54 - 0
packages/cli/src/commands/command-declarations.ts

@@ -0,0 +1,54 @@
+import { CliCommandDefinition } from '../shared/cli-command-definition';
+
+export const cliCommands: CliCommandDefinition[] = [
+    {
+        name: 'add',
+        description: 'Add a feature to your Vendure project',
+        action: async () => {
+            const { addCommand } = await import('./add/add');
+            await addCommand();
+            process.exit(0);
+        },
+    },
+    {
+        name: 'migrate',
+        description: 'Generate, run or revert a database migration',
+        action: async () => {
+            const { migrateCommand } = await import('./migrate/migrate');
+            await migrateCommand();
+            process.exit(0);
+        },
+    },
+    // Example of a command with options
+    {
+        name: 'example',
+        description: 'Example command with options',
+        options: [
+            {
+                flag: '-f, --file <path>',
+                description: 'Path to the file',
+                required: true,
+            },
+            {
+                flag: '-v, --verbose',
+                description: 'Enable verbose output',
+                required: false,
+                defaultValue: false,
+            },
+        ],
+        action: async (options) => {
+            // Example action implementation with options
+            console.log('Example command executed');
+            if (options) {
+                // Validate required options
+                if (!options.file) {
+                    console.error('Error: --file option is required');
+                    process.exit(1);
+                }
+                console.log('File path:', options.file);
+                console.log('Verbose mode:', options.verbose);
+            }
+            process.exit(0);
+        },
+    },
+]; 

+ 17 - 0
packages/cli/src/shared/cli-command-definition.ts

@@ -0,0 +1,17 @@
+export interface CliCommandOption {
+    flag: string;
+    description: string;
+    required?: boolean;
+    defaultValue?: any;
+}
+
+export interface CliCommandDefinition {
+    name: string;
+    description: string;
+    options?: CliCommandOption[];
+    action: (options?: Record<string, any>) => Promise<void>;
+}
+
+export interface CliCommandConfig {
+    commands: CliCommandDefinition[];
+} 

+ 25 - 0
packages/cli/src/shared/command-registry.ts

@@ -0,0 +1,25 @@
+import { Command } from 'commander';
+import { CliCommandDefinition } from './cli-command-definition';
+
+export function registerCommands(program: Command, commands: CliCommandDefinition[]): void {
+    commands.forEach((commandDef) => {
+        const command = program
+            .command(commandDef.name)
+            .description(commandDef.description)
+            .action(async (options) => {
+                await commandDef.action(options);
+            });
+
+        // Add options if they exist
+        if (commandDef.options) {
+            commandDef.options.forEach((option) => {
+                // Handle both required and optional options
+                const optionString = option.required 
+                    ? option.flag
+                    : option.flag.replace(/<([^>]+)>/g, '[$1]');
+                
+                command.option(optionString, option.description, option.defaultValue);
+            });
+        }
+    });
+}