The complete solution for Node.js command-line interfaces
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Command argument system with support for required, optional, and variadic arguments, custom processing, validation, choice restrictions, and comprehensive argument handling.
Create new Argument instances programmatically.
/**
* Creates a new Argument instance
* @param name - Argument name (use <name> for required, [name] for optional)
* @param description - Argument description
* @returns New Argument instance
*/
function createArgument(name: string, description?: string): Argument;Usage Examples:
import { createArgument } from 'commander';
const sourceArg = createArgument('<source>', 'source file path');
const destArg = createArgument('[destination]', 'destination file path')
.default('./output');
const filesArg = createArgument('<files...>', 'input files')
.choices(['jpg', 'png', 'gif']);Represents a command argument with validation and processing.
/**
* Represents a command argument with validation and processing
*/
class Argument {
/**
* Initialize an argument with name and description
* @param name - Argument name with angle brackets for required or square brackets for optional
* @param description - Argument description
*/
constructor(name: string, description?: string);
// Properties
description: string;
required: boolean;
variadic: boolean;
defaultValue?: any;
defaultValueDescription?: string;
parseArg?: Function;
argChoices?: string[];
}Add and configure arguments on commands.
interface Command {
/**
* Define a command argument
* @param name - Argument name (<required> or [optional])
* @param description - Argument description
* @param defaultValue - Default value for optional arguments
* @returns this command for chaining
*/
argument(name: string, description?: string, defaultValue?: unknown): this;
/**
* Define a command argument with custom parser
* @param flags - Argument specification
* @param description - Argument description
* @param parseArg - Custom parsing function
* @param defaultValue - Default value
* @returns this command for chaining
*/
argument<T>(flags: string, description: string, parseArg: (value: string, previous: T) => T, defaultValue?: T): this;
/**
* Define multiple arguments from a string
* @param names - Arguments specification (e.g., '<cmd> [env]')
* @returns this command for chaining
*/
arguments(names: string): this;
/**
* Add a prepared Argument instance
* @param arg - Argument instance to add
* @returns this command for chaining
*/
addArgument(arg: Argument): this;
/**
* Factory for creating Argument instances (can be overridden)
* @param name - Argument name
* @param description - Argument description
* @returns New Argument instance
*/
createArgument(name: string, description?: string): Argument;
}Usage Examples:
program
.command('copy')
// Required argument
.argument('<source>', 'source file')
// Optional argument with default
.argument('[destination]', 'destination file', './output')
// Variadic argument
.argument('<files...>', 'files to process')
.action((source, destination, files) => {
console.log(`Copying ${source} to ${destination}`);
console.log('Additional files:', files);
});
// Multiple arguments from string
program
.command('deploy')
.arguments('<environment> [version]')
.action((environment, version) => {
console.log(`Deploying to ${environment}, version ${version || 'latest'}`);
});
// Argument with custom parser
program
.command('resize')
.argument('<size>', 'image size', (value) => {
const [width, height] = value.split('x').map(Number);
if (!width || !height) {
throw new InvalidArgumentError('Size must be in format WIDTHxHEIGHT');
}
return { width, height };
})
.action((size) => {
console.log(`Resizing to ${size.width}x${size.height}`);
});Configure argument behavior, validation, and processing.
interface Argument {
/**
* Get argument name
* @returns Argument name
*/
name(): string;
/**
* Set default value and optional description
* @param value - Default value
* @param description - Description of default value
* @returns this argument for chaining
*/
default(value: unknown, description?: string): this;
/**
* Set custom argument parser
* @param fn - Parsing function
* @returns this argument for chaining
*/
argParser<T>(fn: (value: string, previous: T) => T): this;
/**
* Restrict argument value to specific choices
* @param values - Allowed values
* @returns this argument for chaining
*/
choices(values: readonly string[]): this;
/**
* Make argument required
* @returns this argument for chaining
*/
argRequired(): this;
/**
* Make argument optional
* @returns this argument for chaining
*/
argOptional(): this;
}Usage Examples:
import { createArgument } from 'commander';
// Argument with default value
const outputArg = createArgument('[output]', 'output directory')
.default('./dist', 'current directory');
// Argument with choices
const formatArg = createArgument('<format>', 'output format')
.choices(['json', 'xml', 'yaml']);
// Argument with custom parser
const coordArg = createArgument('<coordinates>', 'x,y coordinates')
.argParser((value) => {
const [x, y] = value.split(',').map(Number);
if (isNaN(x) || isNaN(y)) {
throw new InvalidArgumentError('Coordinates must be in format x,y');
}
return { x, y };
});
// Variadic argument with validation
const urlsArg = createArgument('<urls...>', 'URLs to process')
.argParser((value, previous) => {
try {
new URL(value);
return (previous || []).concat(value);
} catch {
throw new InvalidArgumentError(`Invalid URL: ${value}`);
}
});Different argument patterns and their behaviors.
Required Arguments:
// Simple required argument
.argument('<filename>', 'file to process')
// Required argument with validation
.argument('<port>', 'port number', (value) => {
const port = parseInt(value);
if (port < 1 || port > 65535) {
throw new InvalidArgumentError('Port must be between 1 and 65535');
}
return port;
})Optional Arguments:
// Optional argument
.argument('[output]', 'output directory')
// Optional argument with default
.argument('[config]', 'configuration file', 'config.json')Variadic Arguments:
// Required variadic (at least one)
.argument('<files...>', 'files to process')
// Optional variadic (zero or more)
.argument('[patterns...]', 'search patterns')Access parsed arguments in action handlers.
interface Command {
// Parsed command-line arguments (with options removed)
args: string[];
// Processed arguments after custom parsing and variadic collection
processedArgs: any[];
}Usage Examples:
program
.command('process')
.argument('<input>', 'input file')
.argument('[output]', 'output file')
.argument('[options...]', 'additional options')
.action((input, output, options) => {
// Arguments passed as separate parameters
console.log('Input:', input);
console.log('Output:', output || 'default.out');
console.log('Options:', options || []);
})
.hook('postAction', (thisCommand) => {
// Access via command properties
console.log('Raw args:', thisCommand.args);
console.log('Processed args:', thisCommand.processedArgs);
});Common patterns for argument validation and processing.
File Path Validation:
import { access, constants } from 'fs/promises';
const inputArg = createArgument('<input>', 'input file')
.argParser(async (value) => {
try {
await access(value, constants.R_OK);
return value;
} catch {
throw new InvalidArgumentError(`Cannot read file: ${value}`);
}
});Number Range Validation:
const percentArg = createArgument('<percent>', 'percentage value')
.argParser((value) => {
const num = parseFloat(value);
if (isNaN(num) || num < 0 || num > 100) {
throw new InvalidArgumentError('Percentage must be between 0 and 100');
}
return num;
});Enum Validation:
const logLevelArg = createArgument('<level>', 'log level')
.choices(['error', 'warn', 'info', 'debug', 'trace']);Complex Object Parsing:
const configArg = createArgument('<config>', 'configuration as key=value pairs')
.argParser((value, previous) => {
const config = previous || {};
const [key, val] = value.split('=');
if (!key || val === undefined) {
throw new InvalidArgumentError('Config must be in format key=value');
}
config[key] = val;
return config;
});Handle argument parsing and validation errors.
import { InvalidArgumentError } from 'commander';
// Custom validation with detailed error messages
const dateArg = createArgument('<date>', 'date in YYYY-MM-DD format')
.argParser((value) => {
const date = new Date(value);
if (isNaN(date.getTime())) {
throw new InvalidArgumentError(`Invalid date format: ${value}. Use YYYY-MM-DD format.`);
}
return date;
});
// Validation with suggestions
const commandArg = createArgument('<command>', 'command to execute')
.choices(['start', 'stop', 'restart', 'status'])
.argParser((value) => {
const valid = ['start', 'stop', 'restart', 'status'];
if (!valid.includes(value)) {
const suggestions = valid.filter(cmd => cmd.startsWith(value.slice(0, 2)));
const suggestion = suggestions.length > 0 ? ` Did you mean: ${suggestions.join(', ')}?` : '';
throw new InvalidArgumentError(`Invalid command: ${value}.${suggestion}`);
}
return value;
});// For variadic arguments, the parser is called for each value
const filesArg = createArgument('<files...>', 'input files')
.argParser((value, previous) => {
// 'previous' contains accumulated values from previous calls
const files = previous || [];
// Validate current file
if (!value.endsWith('.txt')) {
throw new InvalidArgumentError(`File must be .txt: ${value}`);
}
// Return accumulated array
return files.concat(value);
});// Default values are used when argument is not provided
const outputArg = createArgument('[output]', 'output file')
.default('output.txt', 'default output file');
// For variadic arguments, default is used when no arguments provided
const patternsArg = createArgument('[patterns...]', 'search patterns')
.default(['**/*.js'], 'all JavaScript files');