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,261 @@
---
name: yargs-patterns
description: Advanced yargs patterns for Node.js CLI argument parsing with subcommands, options, middleware, and validation
tags: [nodejs, cli, yargs, argument-parsing, validation]
---
# yargs Patterns Skill
Comprehensive patterns and templates for building CLI applications with yargs, the modern Node.js argument parsing library.
## Overview
yargs is a powerful argument parsing library for Node.js that provides:
- Automatic help generation
- Rich command syntax (positional args, options, flags)
- Type coercion and validation
- Subcommands with isolated option namespaces
- Middleware for preprocessing
- Completion scripts for bash/zsh
## Quick Reference
### Basic Setup
```javascript
#!/usr/bin/env node
const yargs = require('yargs/yargs');
const { hideBin } = require('yargs/helpers');
yargs(hideBin(process.argv))
.command('* <name>', 'greet someone', (yargs) => {
yargs.positional('name', {
describe: 'Name to greet',
type: 'string'
});
}, (argv) => {
console.log(`Hello, ${argv.name}!`);
})
.parse();
```
### Subcommands
```javascript
yargs(hideBin(process.argv))
.command('init <project>', 'initialize a new project', (yargs) => {
yargs
.positional('project', {
describe: 'Project name',
type: 'string'
})
.option('template', {
alias: 't',
describe: 'Project template',
choices: ['basic', 'advanced', 'minimal'],
default: 'basic'
});
}, (argv) => {
console.log(`Initializing ${argv.project} with ${argv.template} template`);
})
.command('build [entry]', 'build the project', (yargs) => {
yargs
.positional('entry', {
describe: 'Entry point file',
type: 'string',
default: 'index.js'
})
.option('output', {
alias: 'o',
describe: 'Output directory',
type: 'string',
default: 'dist'
})
.option('minify', {
describe: 'Minify output',
type: 'boolean',
default: false
});
}, (argv) => {
console.log(`Building from ${argv.entry} to ${argv.output}`);
})
.parse();
```
### Options and Flags
```javascript
yargs(hideBin(process.argv))
.option('verbose', {
alias: 'v',
type: 'boolean',
description: 'Run with verbose logging',
default: false
})
.option('config', {
alias: 'c',
type: 'string',
description: 'Path to config file',
demandOption: true // Required option
})
.option('port', {
alias: 'p',
type: 'number',
description: 'Port number',
default: 3000
})
.option('env', {
alias: 'e',
type: 'string',
choices: ['development', 'staging', 'production'],
description: 'Environment'
})
.parse();
```
### Validation
```javascript
yargs(hideBin(process.argv))
.command('deploy <service>', 'deploy a service', (yargs) => {
yargs
.positional('service', {
describe: 'Service name',
type: 'string'
})
.option('version', {
describe: 'Version to deploy',
type: 'string',
coerce: (arg) => {
// Custom validation
if (!/^\d+\.\d+\.\d+$/.test(arg)) {
throw new Error('Version must be in format X.Y.Z');
}
return arg;
}
})
.option('replicas', {
describe: 'Number of replicas',
type: 'number',
default: 1
})
.check((argv) => {
// Cross-field validation
if (argv.replicas > 10 && argv.env === 'development') {
throw new Error('Cannot deploy more than 10 replicas in development');
}
return true;
});
}, (argv) => {
console.log(`Deploying ${argv.service} v${argv.version} with ${argv.replicas} replicas`);
})
.parse();
```
### Middleware
```javascript
yargs(hideBin(process.argv))
.middleware((argv) => {
// Preprocessing middleware
if (argv.verbose) {
console.log('Running in verbose mode');
console.log('Arguments:', argv);
}
})
.middleware((argv) => {
// Load config file
if (argv.config) {
const config = require(path.resolve(argv.config));
return { ...argv, ...config };
}
})
.command('run', 'run the application', {}, (argv) => {
console.log('Application running with config:', argv);
})
.parse();
```
### Advanced Features
#### Conflicts and Implies
```javascript
yargs(hideBin(process.argv))
.option('json', {
describe: 'Output as JSON',
type: 'boolean'
})
.option('yaml', {
describe: 'Output as YAML',
type: 'boolean'
})
.conflicts('json', 'yaml') // Can't use both
.option('output', {
describe: 'Output file',
type: 'string'
})
.option('format', {
describe: 'Output format',
choices: ['json', 'yaml'],
implies: 'output' // format requires output
})
.parse();
```
#### Array Options
```javascript
yargs(hideBin(process.argv))
.option('include', {
describe: 'Files to include',
type: 'array',
default: []
})
.option('exclude', {
describe: 'Files to exclude',
type: 'array',
default: []
})
.parse();
// Usage: cli --include file1.js file2.js --exclude test.js
```
#### Count Options
```javascript
yargs(hideBin(process.argv))
.option('verbose', {
alias: 'v',
describe: 'Verbosity level',
type: 'count' // -v, -vv, -vvv
})
.parse();
```
## Templates
See `templates/` directory for:
- `basic-cli.js` - Simple CLI with commands
- `advanced-cli.js` - Full-featured CLI with validation
- `config-cli.js` - CLI with configuration file support
- `interactive-cli.js` - CLI with prompts
- `plugin-cli.js` - Plugin-based CLI architecture
## Scripts
See `scripts/` directory for:
- `generate-completion.sh` - Generate bash/zsh completion
- `validate-args.js` - Argument validation helper
- `test-cli.sh` - CLI testing script
## Examples
See `examples/` directory for complete working examples.
## Resources
- [yargs Documentation](https://yargs.js.org/)
- [yargs GitHub](https://github.com/yargs/yargs)
- [yargs Best Practices](https://github.com/yargs/yargs/blob/main/docs/tricks.md)

View File

@@ -0,0 +1,89 @@
#!/usr/bin/env node
/**
* Example: yargs with nested subcommands
*
* Usage:
* node subcommands-example.js user create --name John --email john@example.com
* node subcommands-example.js user list --limit 10
* node subcommands-example.js user delete 123
*/
const yargs = require('yargs/yargs');
const { hideBin } = require('yargs/helpers');
yargs(hideBin(process.argv))
.command('user', 'manage users', (yargs) => {
return yargs
.command('create', 'create a new user', (yargs) => {
yargs
.option('name', {
describe: 'User name',
type: 'string',
demandOption: true
})
.option('email', {
describe: 'User email',
type: 'string',
demandOption: true
})
.option('role', {
describe: 'User role',
choices: ['admin', 'user', 'guest'],
default: 'user'
});
}, (argv) => {
console.log(`Creating user: ${argv.name} (${argv.email}) with role ${argv.role}`);
})
.command('list', 'list all users', (yargs) => {
yargs
.option('limit', {
alias: 'l',
describe: 'Limit number of results',
type: 'number',
default: 10
})
.option('offset', {
alias: 'o',
describe: 'Offset for pagination',
type: 'number',
default: 0
});
}, (argv) => {
console.log(`Listing users (limit: ${argv.limit}, offset: ${argv.offset})`);
})
.command('delete <id>', 'delete a user', (yargs) => {
yargs.positional('id', {
describe: 'User ID',
type: 'number'
});
}, (argv) => {
console.log(`Deleting user ID: ${argv.id}`);
})
.demandCommand(1, 'You need to specify a user subcommand');
})
.command('project', 'manage projects', (yargs) => {
return yargs
.command('create <name>', 'create a new project', (yargs) => {
yargs
.positional('name', {
describe: 'Project name',
type: 'string'
})
.option('template', {
alias: 't',
describe: 'Project template',
choices: ['basic', 'advanced'],
default: 'basic'
});
}, (argv) => {
console.log(`Creating project: ${argv.name} with template ${argv.template}`);
})
.command('list', 'list all projects', {}, (argv) => {
console.log('Listing all projects');
})
.demandCommand(1, 'You need to specify a project subcommand');
})
.demandCommand(1, 'You need at least one command')
.strict()
.help()
.parse();

View File

@@ -0,0 +1,37 @@
#!/usr/bin/env bash
# Generate bash/zsh completion script for yargs-based CLI
set -euo pipefail
CLI_NAME="${1:-mycli}"
OUTPUT_FILE="${2:-${CLI_NAME}-completion.sh}"
cat > "$OUTPUT_FILE" <<EOF
#!/usr/bin/env bash
# Bash completion script for ${CLI_NAME}
# Source this file to enable completion: source ${OUTPUT_FILE}
_${CLI_NAME}_completions()
{
local cur prev opts
COMPREPLY=()
cur="\${COMP_WORDS[COMP_CWORD]}"
prev="\${COMP_WORDS[COMP_CWORD-1]}"
# Get completion from yargs
COMP_LINE=\$COMP_LINE COMP_POINT=\$COMP_POINT ${CLI_NAME} --get-yargs-completions "\${COMP_WORDS[@]:1}" 2>/dev/null | while read -r line; do
COMPREPLY+=("\$line")
done
return 0
}
complete -F _${CLI_NAME}_completions ${CLI_NAME}
EOF
chmod +x "$OUTPUT_FILE"
echo "✅ Completion script generated: $OUTPUT_FILE"
echo ""
echo "To enable completion, add this to your ~/.bashrc or ~/.zshrc:"
echo " source $(pwd)/$OUTPUT_FILE"

View File

@@ -0,0 +1,99 @@
#!/usr/bin/env node
const yargs = require('yargs/yargs');
const { hideBin } = require('yargs/helpers');
yargs(hideBin(process.argv))
.command('deploy <service>', 'deploy a service', (yargs) => {
yargs
.positional('service', {
describe: 'Service name to deploy',
type: 'string'
})
.option('environment', {
alias: 'env',
describe: 'Deployment environment',
choices: ['development', 'staging', 'production'],
demandOption: true
})
.option('version', {
alias: 'v',
describe: 'Version to deploy',
type: 'string',
coerce: (arg) => {
if (!/^\d+\.\d+\.\d+$/.test(arg)) {
throw new Error('Version must be in format X.Y.Z');
}
return arg;
}
})
.option('replicas', {
alias: 'r',
describe: 'Number of replicas',
type: 'number',
default: 1
})
.option('force', {
alias: 'f',
describe: 'Force deployment without confirmation',
type: 'boolean',
default: false
})
.check((argv) => {
if (argv.replicas > 10 && argv.environment === 'development') {
throw new Error('Cannot deploy more than 10 replicas in development');
}
if (argv.environment === 'production' && !argv.version) {
throw new Error('Version is required for production deployments');
}
return true;
});
}, (argv) => {
console.log(`Deploying ${argv.service} v${argv.version || 'latest'}`);
console.log(`Environment: ${argv.environment}`);
console.log(`Replicas: ${argv.replicas}`);
if (!argv.force) {
console.log('Use --force to proceed without confirmation');
}
})
.command('rollback <service>', 'rollback a service', (yargs) => {
yargs
.positional('service', {
describe: 'Service name to rollback',
type: 'string'
})
.option('environment', {
alias: 'env',
describe: 'Deployment environment',
choices: ['development', 'staging', 'production'],
demandOption: true
})
.option('version', {
alias: 'v',
describe: 'Version to rollback to',
type: 'string'
});
}, (argv) => {
console.log(`Rolling back ${argv.service} in ${argv.environment}`);
if (argv.version) {
console.log(`Target version: ${argv.version}`);
}
})
.middleware((argv) => {
// Logging middleware
if (argv.verbose) {
console.log('[DEBUG] Arguments:', argv);
}
})
.option('verbose', {
describe: 'Enable verbose logging',
type: 'boolean',
global: true // Available to all commands
})
.demandCommand(1, 'You need at least one command')
.strict()
.help()
.alias('help', 'h')
.version()
.alias('version', 'V')
.parse();

View File

@@ -0,0 +1,28 @@
#!/usr/bin/env node
const yargs = require('yargs/yargs');
const { hideBin } = require('yargs/helpers');
yargs(hideBin(process.argv))
.command('greet <name>', 'greet someone', (yargs) => {
yargs.positional('name', {
describe: 'Name to greet',
type: 'string'
});
}, (argv) => {
console.log(`Hello, ${argv.name}!`);
})
.command('goodbye <name>', 'say goodbye', (yargs) => {
yargs.positional('name', {
describe: 'Name to say goodbye to',
type: 'string'
});
}, (argv) => {
console.log(`Goodbye, ${argv.name}!`);
})
.demandCommand(1, 'You need at least one command')
.strict()
.help()
.alias('help', 'h')
.version()
.alias('version', 'V')
.parse();