Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 09:04:14 +08:00
commit 70c36b5eff
248 changed files with 47482 additions and 0 deletions

View File

@@ -0,0 +1,19 @@
#!/usr/bin/env node
import { Command } from 'commander';
const program = new Command();
program
.name('mycli')
.description('A simple CLI tool')
.version('1.0.0');
program
.command('hello')
.description('Say hello')
.option('-n, --name <name>', 'name to greet', 'World')
.action((options) => {
console.log(`Hello, ${options.name}!`);
});
program.parse();

View File

@@ -0,0 +1,18 @@
import { Command } from 'commander';
const program = new Command();
program
.name('mycli')
.description('A simple CLI tool')
.version('1.0.0');
program
.command('hello')
.description('Say hello')
.option('-n, --name <name>', 'name to greet', 'World')
.action((options) => {
console.log(`Hello, ${options.name}!`);
});
program.parse();

View File

@@ -0,0 +1,93 @@
import { Command } from 'commander';
import chalk from 'chalk';
const program = new Command();
program
.name('mycli')
.description('CLI with various argument types')
.version('1.0.0');
// Command with required argument
program
.command('deploy <environment>')
.description('Deploy to specified environment')
.argument('<environment>', 'target environment (dev, staging, prod)')
.action((environment) => {
console.log(chalk.blue(`Deploying to ${environment}...`));
});
// Command with required and optional arguments
program
.command('create <name> [description]')
.description('Create new item')
.argument('<name>', 'item name')
.argument('[description]', 'item description', 'No description provided')
.action((name, description) => {
console.log(`Creating: ${name}`);
console.log(`Description: ${description}`);
});
// Command with variadic arguments
program
.command('add <items...>')
.description('Add multiple items')
.argument('<items...>', 'items to add')
.action((items) => {
console.log(chalk.blue('Adding items:'));
items.forEach((item, index) => {
console.log(` ${index + 1}. ${item}`);
});
console.log(chalk.green(`✓ Added ${items.length} items`));
});
// Command with custom argument parser
program
.command('wait <seconds>')
.description('Wait for specified time')
.argument('<seconds>', 'seconds to wait', parseFloat)
.action(async (seconds) => {
console.log(chalk.blue(`Waiting ${seconds} seconds...`));
await new Promise((resolve) => setTimeout(resolve, seconds * 1000));
console.log(chalk.green('✓ Done'));
});
// Command with arguments and options
program
.command('copy <source> <destination>')
.description('Copy file from source to destination')
.argument('<source>', 'source file path')
.argument('<destination>', 'destination file path')
.option('-f, --force', 'overwrite if exists', false)
.option('-r, --recursive', 'copy recursively', false)
.action((source, destination, options) => {
console.log(`Copying ${source} to ${destination}`);
console.log('Options:', options);
if (options.force) {
console.log(chalk.yellow('⚠ Force mode: will overwrite existing files'));
}
if (options.recursive) {
console.log(chalk.blue('Recursive copy enabled'));
}
console.log(chalk.green('✓ Copy complete'));
});
// Command with argument validation
program
.command('set-port <port>')
.description('Set application port')
.argument('<port>', 'port number', (value) => {
const port = parseInt(value, 10);
if (isNaN(port)) {
throw new Error('Port must be a number');
}
if (port < 1 || port > 65535) {
throw new Error('Port must be between 1 and 65535');
}
return port;
})
.action((port) => {
console.log(chalk.green(`✓ Port set to ${port}`));
});
program.parse();

View File

@@ -0,0 +1,60 @@
import { Command, Option } from 'commander';
import chalk from 'chalk';
const program = new Command();
program
.name('mycli')
.description('CLI with various option types')
.version('1.0.0');
program
.command('deploy')
.description('Deploy application')
// Boolean flag
.option('-v, --verbose', 'verbose output', false)
// Option with required value
.option('-p, --port <port>', 'port number', '3000')
// Option with optional value
.option('-c, --config [path]', 'config file path')
// Negatable option
.option('--no-build', 'skip build step')
// Multiple choice option using Option class
.addOption(
new Option('-e, --env <environment>', 'target environment')
.choices(['dev', 'staging', 'prod'])
.default('dev')
)
// Option with custom parser
.addOption(
new Option('-r, --replicas <count>', 'number of replicas')
.argParser(parseInt)
.default(3)
)
// Mandatory option
.addOption(
new Option('-t, --token <token>', 'API token')
.makeOptionMandatory()
.env('API_TOKEN')
)
// Variadic option
.option('--tags <tags...>', 'deployment tags')
.action((options) => {
console.log(chalk.blue('Deploying with options:'));
console.log('Verbose:', options.verbose);
console.log('Port:', options.port);
console.log('Config:', options.config);
console.log('Build:', options.build);
console.log('Environment:', options.env);
console.log('Replicas:', options.replicas);
console.log('Token:', options.token ? '***' : 'not set');
console.log('Tags:', options.tags);
if (options.verbose) {
console.log(chalk.gray('Verbose mode enabled'));
}
console.log(chalk.green('✓ Deployment complete'));
});
program.parse();

View File

@@ -0,0 +1,341 @@
import { Command } from 'commander';
import inquirer from 'inquirer';
import chalk from 'chalk';
const program = new Command();
program
.name('mycli')
.description('Interactive CLI with Inquirer.js integration')
.version('1.0.0');
// Interactive init command
program
.command('init')
.description('Initialize project interactively')
.option('-y, --yes', 'skip prompts and use defaults', false)
.action(async (options) => {
if (options.yes) {
console.log(chalk.blue('Using default configuration...'));
await initProject({
name: 'my-project',
template: 'basic',
features: [],
});
return;
}
// Interactive prompts
const answers = await inquirer.prompt([
{
type: 'input',
name: 'name',
message: 'Project name:',
default: 'my-project',
validate: (input) => {
if (!input.trim()) {
return 'Project name is required';
}
if (!/^[a-z0-9-]+$/.test(input)) {
return 'Project name must contain only lowercase letters, numbers, and hyphens';
}
return true;
},
},
{
type: 'list',
name: 'template',
message: 'Choose a template:',
choices: ['basic', 'advanced', 'enterprise'],
default: 'basic',
},
{
type: 'checkbox',
name: 'features',
message: 'Select features:',
choices: [
{ name: 'TypeScript', value: 'typescript', checked: true },
{ name: 'ESLint', value: 'eslint', checked: true },
{ name: 'Prettier', value: 'prettier', checked: true },
{ name: 'Testing (Jest)', value: 'jest' },
{ name: 'CI/CD', value: 'cicd' },
{ name: 'Docker', value: 'docker' },
],
},
{
type: 'confirm',
name: 'install',
message: 'Install dependencies now?',
default: true,
},
]);
await initProject(answers);
});
// Interactive deploy command
program
.command('deploy')
.description('Deploy with interactive configuration')
.option('-e, --env <environment>', 'skip environment prompt')
.action(async (options) => {
const questions: any[] = [];
// Conditionally add environment question
if (!options.env) {
questions.push({
type: 'list',
name: 'environment',
message: 'Select deployment environment:',
choices: ['dev', 'staging', 'prod'],
});
}
questions.push(
{
type: 'list',
name: 'strategy',
message: 'Deployment strategy:',
choices: ['rolling', 'blue-green', 'canary'],
default: 'rolling',
},
{
type: 'confirm',
name: 'runTests',
message: 'Run tests before deployment?',
default: true,
},
{
type: 'confirm',
name: 'createBackup',
message: 'Create backup before deployment?',
default: true,
when: (answers) => answers.environment === 'prod',
},
{
type: 'password',
name: 'token',
message: 'Enter deployment token:',
mask: '*',
validate: (input) => (input.length > 0 ? true : 'Token is required'),
}
);
const answers = await inquirer.prompt(questions);
const deployConfig = {
environment: options.env || answers.environment,
...answers,
};
console.log(chalk.blue('\nDeployment configuration:'));
console.log(JSON.stringify(deployConfig, null, 2));
const { confirm } = await inquirer.prompt([
{
type: 'confirm',
name: 'confirm',
message: 'Proceed with deployment?',
default: false,
},
]);
if (!confirm) {
console.log(chalk.yellow('Deployment cancelled'));
return;
}
await deploy(deployConfig);
});
// Interactive config command
program
.command('config')
.description('Configure application interactively')
.action(async () => {
const mainMenu = await inquirer.prompt([
{
type: 'list',
name: 'action',
message: 'What would you like to do?',
choices: [
{ name: 'View configuration', value: 'view' },
{ name: 'Edit configuration', value: 'edit' },
{ name: 'Reset to defaults', value: 'reset' },
{ name: 'Exit', value: 'exit' },
],
},
]);
switch (mainMenu.action) {
case 'view':
console.log(chalk.blue('\nCurrent configuration:'));
console.log(JSON.stringify(getConfig(), null, 2));
break;
case 'edit':
const config = await inquirer.prompt([
{
type: 'input',
name: 'apiUrl',
message: 'API URL:',
default: getConfig().apiUrl,
},
{
type: 'number',
name: 'timeout',
message: 'Request timeout (ms):',
default: getConfig().timeout,
},
{
type: 'list',
name: 'logLevel',
message: 'Log level:',
choices: ['debug', 'info', 'warn', 'error'],
default: getConfig().logLevel,
},
]);
saveConfig(config);
console.log(chalk.green('✓ Configuration saved'));
break;
case 'reset':
const { confirmReset } = await inquirer.prompt([
{
type: 'confirm',
name: 'confirmReset',
message: 'Reset to default configuration?',
default: false,
},
]);
if (confirmReset) {
resetConfig();
console.log(chalk.green('✓ Configuration reset'));
}
break;
case 'exit':
console.log(chalk.gray('Goodbye!'));
break;
}
});
// Multi-step wizard
program
.command('wizard')
.description('Run setup wizard')
.action(async () => {
console.log(chalk.blue('🧙 Welcome to the setup wizard\n'));
// Step 1: Basic info
console.log(chalk.bold('Step 1: Basic Information'));
const step1 = await inquirer.prompt([
{
type: 'input',
name: 'projectName',
message: 'Project name:',
},
{
type: 'input',
name: 'description',
message: 'Description:',
},
]);
// Step 2: Technology stack
console.log(chalk.bold('\nStep 2: Technology Stack'));
const step2 = await inquirer.prompt([
{
type: 'list',
name: 'language',
message: 'Primary language:',
choices: ['TypeScript', 'JavaScript', 'Python', 'Go', 'Rust'],
},
{
type: 'checkbox',
name: 'frameworks',
message: 'Select frameworks:',
choices: (answers) => {
const frameworksByLanguage: Record<string, string[]> = {
TypeScript: ['Next.js', 'Express', 'NestJS'],
JavaScript: ['React', 'Vue', 'Express'],
Python: ['FastAPI', 'Django', 'Flask'],
Go: ['Gin', 'Echo', 'Fiber'],
Rust: ['Actix', 'Rocket', 'Axum'],
};
return frameworksByLanguage[answers.language] || [];
},
},
]);
// Step 3: Infrastructure
console.log(chalk.bold('\nStep 3: Infrastructure'));
const step3 = await inquirer.prompt([
{
type: 'list',
name: 'database',
message: 'Database:',
choices: ['PostgreSQL', 'MySQL', 'MongoDB', 'SQLite', 'None'],
},
{
type: 'checkbox',
name: 'services',
message: 'Additional services:',
choices: ['Redis', 'ElasticSearch', 'RabbitMQ', 'S3'],
},
]);
// Summary
const config = { ...step1, ...step2, ...step3 };
console.log(chalk.bold('\n📋 Configuration Summary:'));
console.log(JSON.stringify(config, null, 2));
const { confirm } = await inquirer.prompt([
{
type: 'confirm',
name: 'confirm',
message: 'Create project with this configuration?',
default: true,
},
]);
if (confirm) {
console.log(chalk.green('\n✓ Project created successfully!'));
} else {
console.log(chalk.yellow('\n✗ Project creation cancelled'));
}
});
// Helper functions
async function initProject(config: any) {
console.log(chalk.blue('\nInitializing project...'));
console.log('Name:', config.name);
console.log('Template:', config.template);
console.log('Features:', config.features.join(', '));
console.log(chalk.green('\n✓ Project initialized!'));
}
async function deploy(config: any) {
console.log(chalk.blue('\nDeploying...'));
console.log(JSON.stringify(config, null, 2));
console.log(chalk.green('\n✓ Deployment complete!'));
}
function getConfig() {
return {
apiUrl: 'https://api.example.com',
timeout: 5000,
logLevel: 'info',
};
}
function saveConfig(config: any) {
console.log('Saving config:', config);
}
function resetConfig() {
console.log('Resetting config to defaults');
}
program.parse();

View File

@@ -0,0 +1,266 @@
import { Command, Option } from 'commander';
import chalk from 'chalk';
import { z } from 'zod';
const program = new Command();
program
.name('mycli')
.description('CLI with comprehensive input validation')
.version('1.0.0');
// Zod schemas for validation
const EmailSchema = z.string().email();
const UrlSchema = z.string().url();
const PortSchema = z.number().int().min(1).max(65535);
const EnvironmentSchema = z.enum(['dev', 'staging', 'prod']);
// Validation helper
function validateOrThrow<T>(schema: z.ZodSchema<T>, value: unknown, fieldName: string): T {
try {
return schema.parse(value);
} catch (error) {
if (error instanceof z.ZodError) {
throw new Error(`Invalid ${fieldName}: ${error.errors[0].message}`);
}
throw error;
}
}
// Command with validated arguments
program
.command('create-user <email> <username>')
.description('Create user with validated inputs')
.argument('<email>', 'user email', (value) => {
return validateOrThrow(EmailSchema, value, 'email');
})
.argument('<username>', 'username', (value) => {
const UsernameSchema = z
.string()
.min(3, 'Username must be at least 3 characters')
.max(20, 'Username must be at most 20 characters')
.regex(/^[a-z0-9_]+$/, 'Username must contain only lowercase letters, numbers, and underscores');
return validateOrThrow(UsernameSchema, value, 'username');
})
.option('-a, --age <age>', 'user age', (value) => {
const age = parseInt(value, 10);
const AgeSchema = z.number().int().min(13).max(120);
return validateOrThrow(AgeSchema, age, 'age');
})
.action((email, username, options) => {
console.log(chalk.green('✓ User validation passed'));
console.log('Email:', email);
console.log('Username:', username);
if (options.age) {
console.log('Age:', options.age);
}
});
// Command with validated options
program
.command('deploy')
.description('Deploy with validated configuration')
.addOption(
new Option('-e, --env <environment>', 'deployment environment')
.argParser((value) => {
return validateOrThrow(EnvironmentSchema, value, 'environment');
})
.makeOptionMandatory()
)
.addOption(
new Option('-u, --url <url>', 'deployment URL')
.argParser((value) => {
return validateOrThrow(UrlSchema, value, 'URL');
})
.makeOptionMandatory()
)
.addOption(
new Option('-p, --port <port>', 'server port')
.argParser((value) => {
const port = parseInt(value, 10);
return validateOrThrow(PortSchema, port, 'port');
})
.default(3000)
)
.addOption(
new Option('-r, --replicas <count>', 'replica count')
.argParser((value) => {
const count = parseInt(value, 10);
const ReplicaSchema = z.number().int().min(1).max(100);
return validateOrThrow(ReplicaSchema, count, 'replicas');
})
.default(3)
)
.action((options) => {
console.log(chalk.green('✓ Deployment configuration validated'));
console.log('Environment:', options.env);
console.log('URL:', options.url);
console.log('Port:', options.port);
console.log('Replicas:', options.replicas);
});
// Command with complex object validation
program
.command('configure')
.description('Configure application with JSON validation')
.option('-c, --config <json>', 'configuration as JSON string', (value) => {
// Parse JSON
let parsed;
try {
parsed = JSON.parse(value);
} catch {
throw new Error('Invalid JSON format');
}
// Validate with Zod schema
const ConfigSchema = z.object({
api: z.object({
url: z.string().url(),
timeout: z.number().int().positive(),
retries: z.number().int().min(0).max(5),
}),
database: z.object({
host: z.string(),
port: z.number().int().min(1).max(65535),
name: z.string(),
}),
features: z.object({
enableCache: z.boolean(),
enableMetrics: z.boolean(),
}),
});
return validateOrThrow(ConfigSchema, parsed, 'configuration');
})
.action((options) => {
if (options.config) {
console.log(chalk.green('✓ Configuration validated'));
console.log(JSON.stringify(options.config, null, 2));
}
});
// Command with file path validation
program
.command('process <inputFile> <outputFile>')
.description('Process file with path validation')
.argument('<inputFile>', 'input file path', (value) => {
const FilePathSchema = z.string().refine(
(path) => {
// Check file extension
return path.endsWith('.json') || path.endsWith('.yaml') || path.endsWith('.yml');
},
{ message: 'File must be .json, .yaml, or .yml' }
);
return validateOrThrow(FilePathSchema, value, 'input file');
})
.argument('<outputFile>', 'output file path', (value) => {
const FilePathSchema = z.string().min(1);
return validateOrThrow(FilePathSchema, value, 'output file');
})
.action((inputFile, outputFile) => {
console.log(chalk.green('✓ File paths validated'));
console.log('Input:', inputFile);
console.log('Output:', outputFile);
});
// Command with date/time validation
program
.command('schedule <datetime>')
.description('Schedule task with datetime validation')
.argument('<datetime>', 'ISO 8601 datetime', (value) => {
const date = new Date(value);
if (isNaN(date.getTime())) {
throw new Error('Invalid datetime format (use ISO 8601)');
}
if (date < new Date()) {
throw new Error('Datetime must be in the future');
}
return date;
})
.option('-d, --duration <minutes>', 'duration in minutes', (value) => {
const minutes = parseInt(value, 10);
const DurationSchema = z.number().int().min(1).max(1440); // 1 min to 24 hours
return validateOrThrow(DurationSchema, minutes, 'duration');
})
.action((datetime, options) => {
console.log(chalk.green('✓ Schedule validated'));
console.log('Start time:', datetime.toISOString());
if (options.duration) {
const endTime = new Date(datetime.getTime() + options.duration * 60000);
console.log('End time:', endTime.toISOString());
}
});
// Command with array validation
program
.command('tag <items...>')
.description('Tag items with validation')
.argument('<items...>', 'items to tag')
.option('-t, --tags <tags...>', 'tags to apply', (values) => {
const TagSchema = z.array(
z
.string()
.min(2)
.max(20)
.regex(/^[a-z0-9-]+$/, 'Tags must contain only lowercase letters, numbers, and hyphens')
);
return validateOrThrow(TagSchema, values, 'tags');
})
.action((items, options) => {
console.log(chalk.green('✓ Items and tags validated'));
console.log('Items:', items);
if (options.tags) {
console.log('Tags:', options.tags);
}
});
// Command with custom validation logic
program
.command('migrate')
.description('Database migration with version validation')
.option('-f, --from <version>', 'migrate from version', (value) => {
const VersionSchema = z.string().regex(/^\d+\.\d+\.\d+$/, 'Version must be in format X.Y.Z');
return validateOrThrow(VersionSchema, value, 'from version');
})
.option('-t, --to <version>', 'migrate to version', (value) => {
const VersionSchema = z.string().regex(/^\d+\.\d+\.\d+$/, 'Version must be in format X.Y.Z');
return validateOrThrow(VersionSchema, value, 'to version');
})
.action((options) => {
// Additional cross-field validation
if (options.from && options.to) {
const fromParts = options.from.split('.').map(Number);
const toParts = options.to.split('.').map(Number);
const fromNum = fromParts[0] * 10000 + fromParts[1] * 100 + fromParts[2];
const toNum = toParts[0] * 10000 + toParts[1] * 100 + toParts[2];
if (fromNum >= toNum) {
throw new Error('Target version must be higher than source version');
}
console.log(chalk.green('✓ Migration versions validated'));
console.log(`Migrating from ${options.from} to ${options.to}`);
}
});
// Global error handling
program.exitOverride();
try {
program.parse();
} catch (error: any) {
if (error.code === 'commander.help' || error.code === 'commander.version') {
process.exit(0);
} else {
console.error(chalk.red('Validation Error:'), error.message);
console.error(chalk.gray('\nUse --help for usage information'));
process.exit(1);
}
}

View File

@@ -0,0 +1,30 @@
#!/usr/bin/env node
const { Command } = require('commander');
const program = new Command();
program
.name('mycli')
.description('A simple CLI tool (CommonJS)')
.version('1.0.0');
program
.command('hello')
.description('Say hello')
.option('-n, --name <name>', 'name to greet', 'World')
.action((options) => {
console.log(`Hello, ${options.name}!`);
});
program
.command('deploy <environment>')
.description('Deploy to environment')
.option('-f, --force', 'force deployment', false)
.action((environment, options) => {
console.log(`Deploying to ${environment}...`);
if (options.force) {
console.log('Force mode enabled');
}
});
program.parse();

View File

@@ -0,0 +1,282 @@
#!/usr/bin/env node
import { Command, Option } from 'commander';
import chalk from 'chalk';
import ora from 'ora';
const program = new Command();
// Configure main program
program
.name('mycli')
.description('A powerful CLI tool with all Commander.js features')
.version('1.0.0')
.option('-c, --config <path>', 'config file path', './config.json')
.option('-v, --verbose', 'verbose output', false)
.option('--no-color', 'disable colored output');
// Init command
program
.command('init')
.description('Initialize a new project')
.option('-t, --template <type>', 'project template', 'basic')
.option('-d, --directory <path>', 'target directory', '.')
.option('-f, --force', 'overwrite existing files', false)
.action(async (options) => {
const spinner = ora('Initializing project...').start();
try {
await new Promise((resolve) => setTimeout(resolve, 1000));
spinner.text = 'Creating directory structure...';
await new Promise((resolve) => setTimeout(resolve, 500));
spinner.text = 'Copying template files...';
await new Promise((resolve) => setTimeout(resolve, 500));
spinner.text = 'Installing dependencies...';
await new Promise((resolve) => setTimeout(resolve, 1000));
spinner.succeed(chalk.green('Project initialized successfully!'));
console.log(chalk.blue('\nNext steps:'));
console.log(` cd ${options.directory}`);
console.log(' mycli dev');
} catch (error) {
spinner.fail(chalk.red('Initialization failed'));
throw error;
}
});
// Dev command
program
.command('dev')
.description('Start development server')
.option('-p, --port <port>', 'server port', '3000')
.option('-h, --host <host>', 'server host', 'localhost')
.option('--open', 'open browser automatically', false)
.action((options) => {
console.log(chalk.blue('Starting development server...'));
console.log(`Server running at http://${options.host}:${options.port}`);
if (options.open) {
console.log(chalk.gray('Opening browser...'));
}
});
// Build command
program
.command('build')
.description('Build for production')
.addOption(
new Option('-m, --mode <mode>', 'build mode')
.choices(['development', 'production'])
.default('production')
)
.option('--analyze', 'analyze bundle size', false)
.option('--sourcemap', 'generate source maps', false)
.action(async (options) => {
const spinner = ora('Building for production...').start();
try {
await new Promise((resolve) => setTimeout(resolve, 2000));
spinner.succeed(chalk.green('Build complete!'));
console.log(chalk.blue('\nBuild info:'));
console.log('Mode:', options.mode);
console.log('Source maps:', options.sourcemap ? 'enabled' : 'disabled');
if (options.analyze) {
console.log(chalk.gray('\nBundle analysis:'));
console.log(' main.js: 245 KB');
console.log(' vendor.js: 892 KB');
}
} catch (error) {
spinner.fail(chalk.red('Build failed'));
throw error;
}
});
// Deploy command with nested subcommands
const deploy = program.command('deploy').description('Deployment operations');
deploy
.command('start <environment>')
.description('Deploy to specified environment')
.argument('<environment>', 'target environment (dev, staging, prod)')
.addOption(
new Option('-s, --strategy <strategy>', 'deployment strategy')
.choices(['rolling', 'blue-green', 'canary'])
.default('rolling')
)
.option('-f, --force', 'force deployment', false)
.option('--dry-run', 'simulate deployment', false)
.action(async (environment, options) => {
if (options.dryRun) {
console.log(chalk.yellow('🔍 Dry run mode - no actual deployment'));
}
const spinner = ora(`Deploying to ${environment}...`).start();
try {
spinner.text = 'Running tests...';
await new Promise((resolve) => setTimeout(resolve, 1000));
spinner.text = 'Building application...';
await new Promise((resolve) => setTimeout(resolve, 1500));
spinner.text = `Deploying with ${options.strategy} strategy...`;
await new Promise((resolve) => setTimeout(resolve, 2000));
spinner.succeed(chalk.green(`Deployed to ${environment}!`));
console.log(chalk.blue('\nDeployment details:'));
console.log('Environment:', environment);
console.log('Strategy:', options.strategy);
console.log('URL:', `https://${environment}.example.com`);
} catch (error) {
spinner.fail(chalk.red('Deployment failed'));
throw error;
}
});
deploy
.command('rollback [version]')
.description('Rollback to previous version')
.argument('[version]', 'version to rollback to', 'previous')
.option('-f, --force', 'skip confirmation', false)
.action((version, options) => {
if (!options.force) {
console.log(chalk.yellow('⚠ This will rollback your deployment. Use --force to confirm.'));
return;
}
console.log(chalk.blue(`Rolling back to ${version}...`));
console.log(chalk.green('✓ Rollback complete'));
});
deploy
.command('status')
.description('Check deployment status')
.option('-e, --env <environment>', 'check specific environment')
.action((options) => {
console.log(chalk.blue('Deployment status:'));
const envs = options.env ? [options.env] : ['dev', 'staging', 'prod'];
envs.forEach((env) => {
console.log(`\n${env}:`);
console.log(' Status:', chalk.green('healthy'));
console.log(' Version:', '1.2.3');
console.log(' Uptime:', '5d 12h 34m');
});
});
// Config command group
const config = program.command('config').description('Configuration management');
config
.command('get <key>')
.description('Get configuration value')
.action((key) => {
console.log(`${key}: value`);
});
config
.command('set <key> <value>')
.description('Set configuration value')
.action((key, value) => {
console.log(chalk.green(`✓ Set ${key} = ${value}`));
});
config
.command('list')
.description('List all configuration')
.option('-f, --format <format>', 'output format (json, yaml, table)', 'table')
.action((options) => {
console.log(`Configuration (format: ${options.format}):`);
console.log('key1: value1');
console.log('key2: value2');
});
// Database command group
const db = program.command('db').description('Database operations');
db.command('migrate')
.description('Run database migrations')
.option('-d, --dry-run', 'show migrations without running')
.action((options) => {
if (options.dryRun) {
console.log(chalk.blue('Migrations to run:'));
console.log(' 001_create_users.sql');
console.log(' 002_add_email_index.sql');
} else {
console.log(chalk.blue('Running migrations...'));
console.log(chalk.green('✓ 2 migrations applied'));
}
});
db.command('seed')
.description('Seed database with data')
.option('-e, --env <env>', 'environment', 'dev')
.action((options) => {
console.log(chalk.blue(`Seeding ${options.env} database...`));
console.log(chalk.green('✓ Database seeded'));
});
// Test command
program
.command('test [pattern]')
.description('Run tests')
.argument('[pattern]', 'test file pattern', '**/*.test.ts')
.option('-w, --watch', 'watch mode', false)
.option('-c, --coverage', 'collect coverage', false)
.option('--verbose', 'verbose output', false)
.action((pattern, options) => {
console.log(chalk.blue('Running tests...'));
console.log('Pattern:', pattern);
if (options.watch) {
console.log(chalk.gray('Watch mode enabled'));
}
if (options.coverage) {
console.log(chalk.gray('\nCoverage:'));
console.log(' Statements: 85%');
console.log(' Branches: 78%');
console.log(' Functions: 92%');
console.log(' Lines: 87%');
}
console.log(chalk.green('\n✓ 42 tests passed'));
});
// Global error handling
program.exitOverride();
try {
program.parse();
} catch (error: any) {
if (error.code === 'commander.help') {
// Help was displayed, exit normally
process.exit(0);
} else if (error.code === 'commander.version') {
// Version was displayed, exit normally
process.exit(0);
} else {
console.error(chalk.red('Error:'), error.message);
const globalOpts = program.opts();
if (globalOpts.verbose) {
console.error(chalk.gray('\nStack trace:'));
console.error(chalk.gray(error.stack));
}
process.exit(1);
}
}
// Handle no command
if (process.argv.length <= 2) {
program.help();
}

View File

@@ -0,0 +1,150 @@
import { Command } from 'commander';
import chalk from 'chalk';
const program = new Command();
program
.name('mycli')
.description('CLI with nested subcommands')
.version('1.0.0');
// Config command group
const config = program.command('config').description('Manage configuration');
config
.command('get <key>')
.description('Get configuration value')
.action((key) => {
const value = getConfig(key);
if (value) {
console.log(`${key} = ${value}`);
} else {
console.log(chalk.yellow(`Config key '${key}' not found`));
}
});
config
.command('set <key> <value>')
.description('Set configuration value')
.action((key, value) => {
setConfig(key, value);
console.log(chalk.green(`✓ Set ${key} = ${value}`));
});
config
.command('list')
.description('List all configuration')
.option('-f, --format <format>', 'output format (json, table)', 'table')
.action((options) => {
const allConfig = getAllConfig();
if (options.format === 'json') {
console.log(JSON.stringify(allConfig, null, 2));
} else {
Object.entries(allConfig).forEach(([key, value]) => {
console.log(`${key.padEnd(20)} ${value}`);
});
}
});
config
.command('delete <key>')
.description('Delete configuration value')
.option('-f, --force', 'skip confirmation', false)
.action((key, options) => {
if (!options.force) {
console.log(chalk.yellow(`Are you sure? Use --force to confirm`));
return;
}
deleteConfig(key);
console.log(chalk.green(`✓ Deleted ${key}`));
});
// Database command group
const db = program.command('db').description('Database operations');
db.command('migrate')
.description('Run database migrations')
.option('-d, --dry-run', 'show what would be migrated')
.action((options) => {
if (options.dryRun) {
console.log(chalk.blue('Dry run: showing migrations...'));
} else {
console.log(chalk.blue('Running migrations...'));
}
console.log(chalk.green('✓ Migrations complete'));
});
db.command('seed')
.description('Seed database with data')
.option('-e, --env <env>', 'environment', 'dev')
.action((options) => {
console.log(chalk.blue(`Seeding ${options.env} database...`));
console.log(chalk.green('✓ Seeding complete'));
});
db.command('reset')
.description('Reset database')
.option('-f, --force', 'skip confirmation')
.action((options) => {
if (!options.force) {
console.log(chalk.red('⚠ This will delete all data! Use --force to confirm'));
return;
}
console.log(chalk.yellow('Resetting database...'));
console.log(chalk.green('✓ Database reset'));
});
// User command group with nested subcommands
const user = program.command('user').description('User management');
const userList = user.command('list').description('List users');
userList
.option('-p, --page <page>', 'page number', '1')
.option('-l, --limit <limit>', 'items per page', '10')
.action((options) => {
console.log(`Listing users (page ${options.page}, limit ${options.limit})`);
});
user
.command('create <username> <email>')
.description('Create new user')
.option('-r, --role <role>', 'user role', 'user')
.action((username, email, options) => {
console.log(chalk.green(`✓ Created user ${username} (${email}) with role ${options.role}`));
});
user
.command('delete <userId>')
.description('Delete user')
.option('-f, --force', 'skip confirmation')
.action((userId, options) => {
if (!options.force) {
console.log(chalk.red('Use --force to confirm deletion'));
return;
}
console.log(chalk.green(`✓ Deleted user ${userId}`));
});
// Helper functions (mock implementations)
const configStore: Record<string, string> = {
apiUrl: 'https://api.example.com',
timeout: '5000',
};
function getConfig(key: string): string | undefined {
return configStore[key];
}
function setConfig(key: string, value: string): void {
configStore[key] = value;
}
function getAllConfig(): Record<string, string> {
return { ...configStore };
}
function deleteConfig(key: string): void {
delete configStore[key];
}
program.parse();

View File

@@ -0,0 +1,213 @@
import { Command, Option } from 'commander';
import chalk from 'chalk';
const program = new Command();
program
.name('mycli')
.description('Advanced Option class usage')
.version('1.0.0');
program
.command('deploy')
.description('Deploy with advanced option patterns')
// Option with choices
.addOption(
new Option('-e, --env <environment>', 'deployment environment')
.choices(['dev', 'staging', 'prod'])
.default('dev')
)
// Option with custom parser and validation
.addOption(
new Option('-p, --port <port>', 'server port')
.argParser((value) => {
const port = parseInt(value, 10);
if (isNaN(port) || port < 1 || port > 65535) {
throw new Error('Port must be between 1 and 65535');
}
return port;
})
.default(3000)
)
// Mandatory option
.addOption(
new Option('-t, --token <token>', 'API authentication token')
.makeOptionMandatory()
.env('API_TOKEN')
)
// Option from environment variable
.addOption(
new Option('-u, --api-url <url>', 'API base URL')
.env('API_URL')
.default('https://api.example.com')
)
// Conflicting options
.addOption(
new Option('--use-cache', 'enable caching')
.conflicts('noCache')
)
.addOption(
new Option('--no-cache', 'disable caching')
.conflicts('useCache')
)
// Implies relationship
.addOption(
new Option('--ssl', 'enable SSL')
.implies({ sslVerify: true })
)
.addOption(
new Option('--ssl-verify', 'verify SSL certificates')
)
// Preset configurations
.addOption(
new Option('--preset <preset>', 'use preset configuration')
.choices(['minimal', 'standard', 'complete'])
.argParser((value) => {
const presets = {
minimal: { replicas: 1, timeout: 30 },
standard: { replicas: 3, timeout: 60 },
complete: { replicas: 5, timeout: 120 },
};
return presets[value as keyof typeof presets];
})
)
// Hidden option (for debugging)
.addOption(
new Option('--debug', 'enable debug mode')
.hideHelp()
)
// Custom option processing
.addOption(
new Option('-r, --replicas <count>', 'number of replicas')
.argParser((value, previous) => {
const count = parseInt(value, 10);
if (count < 1) {
throw new Error('Replicas must be at least 1');
}
return count;
})
.default(3)
)
.action((options) => {
console.log(chalk.blue('Deployment configuration:'));
console.log('Environment:', chalk.yellow(options.env));
console.log('Port:', options.port);
console.log('Token:', options.token ? chalk.green('***set***') : chalk.red('not set'));
console.log('API URL:', options.apiUrl);
console.log('Cache:', options.useCache ? 'enabled' : 'disabled');
console.log('SSL:', options.ssl ? 'enabled' : 'disabled');
console.log('SSL Verify:', options.sslVerify ? 'enabled' : 'disabled');
if (options.preset) {
console.log('Preset configuration:', options.preset);
}
console.log('Replicas:', options.replicas);
if (options.debug) {
console.log(chalk.gray('\nDebug mode enabled'));
console.log(chalk.gray('All options:'), options);
}
console.log(chalk.green('\n✓ Deployment configuration validated'));
});
// Command demonstrating option dependencies
program
.command('backup')
.description('Backup data with option dependencies')
.addOption(
new Option('-m, --method <method>', 'backup method')
.choices(['full', 'incremental', 'differential'])
.makeOptionMandatory()
)
.addOption(
new Option('--base-backup <path>', 'base backup for incremental')
.conflicts('full')
)
.addOption(
new Option('--compression <level>', 'compression level')
.choices(['none', 'low', 'medium', 'high'])
.default('medium')
)
.action((options) => {
if (options.method === 'incremental' && !options.baseBackup) {
console.log(chalk.red('Error: --base-backup required for incremental backup'));
process.exit(1);
}
console.log(chalk.blue(`Running ${options.method} backup...`));
console.log('Compression:', options.compression);
if (options.baseBackup) {
console.log('Base backup:', options.baseBackup);
}
console.log(chalk.green('✓ Backup complete'));
});
// Command with complex validation
program
.command('scale')
.description('Scale application with complex validation')
.addOption(
new Option('-r, --replicas <count>', 'number of replicas')
.argParser((value) => {
const count = parseInt(value, 10);
if (isNaN(count)) {
throw new Error('Replicas must be a number');
}
if (count < 1 || count > 100) {
throw new Error('Replicas must be between 1 and 100');
}
return count;
})
.makeOptionMandatory()
)
.addOption(
new Option('--cpu <value>', 'CPU limit (millicores)')
.argParser((value) => {
const cpu = parseInt(value, 10);
if (cpu < 100 || cpu > 8000) {
throw new Error('CPU must be between 100 and 8000 millicores');
}
return cpu;
})
.default(1000)
)
.addOption(
new Option('--memory <value>', 'Memory limit (MB)')
.argParser((value) => {
const mem = parseInt(value, 10);
if (mem < 128 || mem > 16384) {
throw new Error('Memory must be between 128 and 16384 MB');
}
return mem;
})
.default(512)
)
.action((options) => {
console.log(chalk.blue('Scaling application:'));
console.log('Replicas:', chalk.yellow(options.replicas));
console.log('CPU:', `${options.cpu}m`);
console.log('Memory:', `${options.memory}MB`);
const totalCpu = options.replicas * options.cpu;
const totalMemory = options.replicas * options.memory;
console.log(chalk.gray('\nTotal resources:'));
console.log(chalk.gray(`CPU: ${totalCpu}m`));
console.log(chalk.gray(`Memory: ${totalMemory}MB`));
console.log(chalk.green('\n✓ Scaling complete'));
});
program.parse();

View File

@@ -0,0 +1,46 @@
{
"name": "{{CLI_NAME}}",
"version": "1.0.0",
"description": "{{DESCRIPTION}}",
"type": "module",
"main": "dist/index.js",
"bin": {
"{{CLI_NAME}}": "dist/index.js"
},
"scripts": {
"build": "tsc",
"dev": "tsx watch src/index.ts",
"start": "node dist/index.js",
"lint": "eslint src --ext .ts",
"format": "prettier --write 'src/**/*.ts'",
"test": "vitest",
"prepublishOnly": "npm run build"
},
"keywords": [
"cli",
"commander",
"typescript"
],
"author": "{{AUTHOR}}",
"license": "MIT",
"dependencies": {
"commander": "^12.0.0",
"chalk": "^5.3.0",
"ora": "^8.0.1",
"inquirer": "^9.2.15"
},
"devDependencies": {
"@types/node": "^20.11.24",
"@types/inquirer": "^9.0.7",
"typescript": "^5.3.3",
"tsx": "^4.7.1",
"eslint": "^8.57.0",
"@typescript-eslint/eslint-plugin": "^7.1.0",
"@typescript-eslint/parser": "^7.1.0",
"prettier": "^3.2.5",
"vitest": "^1.3.1"
},
"engines": {
"node": ">=18.0.0"
}
}

View File

@@ -0,0 +1,26 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"lib": ["ES2022"],
"moduleResolution": "node",
"resolveJsonModule": true,
"allowJs": true,
"outDir": "./dist",
"rootDir": "./src",
"removeComments": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}