commander
Advanced tools
Comparing version 2.20.0 to 3.0.0-0
@@ -0,2 +1,28 @@ | ||
3.0.0-0 Prerelease | ||
===== | ||
* Add option to specify executable file name (#999) | ||
* e.g. `.command('clone', 'clone description', { executableFile: 'myClone' })` | ||
* Change docs for `.command` to contrast action handler vs git-style executable. TypeScript now uses overloaded function. (#938 #990) | ||
* Change to use straight quotes around strings in error messages (like 'this' instead of `this') (#915) | ||
* Add TypeScript "reference types" for node (#974) | ||
* Add support for hyphen as an option argument in subcommands (#697) | ||
* Add support for a short option flag and its value to be concatenated for action handler subcommands (#599) | ||
* e.g. `-p 80` can also be supplied as `-p80` | ||
* Add executable arguments to spawn in win32, for git-style executables (#611) | ||
* e.g. `node --harmony myCommand.js clone` | ||
* Add parent command as prefix of subcommand in help (#980) | ||
* Add optional custom description to `.version` (#963) | ||
* e.g. `program.version('0.0.1', '-v, --vers', 'output the current version')` | ||
* Add `.helpOption(flags, description)` routine to customise help flags and description (#963) | ||
* e.g. `.helpOption('-e, --HELP', 'read more information')` | ||
* Fix behavior of --no-* options (#795) | ||
* can now define both `--foo` and `--no-foo` | ||
* custom event listeners: `--no-foo` on cli now emits `option:no-foo` (previously `option:foo`) | ||
* default value: defining `--no-foo` after defining `--foo` leaves the default value unchanged (previously set it to false) | ||
* allow boolean default value, such as from environment (#987) | ||
* Increment inspector port for spawned subcommands (#991) | ||
* e.g. `node --inspect myCommand.js clone` | ||
2.20.0 / 2019-04-02 | ||
@@ -3,0 +29,0 @@ ================== |
334
index.js
@@ -48,3 +48,3 @@ /** | ||
this.optional = flags.indexOf('[') >= 0; | ||
this.bool = flags.indexOf('-no-') === -1; | ||
this.negate = flags.indexOf('-no-') !== -1; | ||
flags = flags.split(/[ ,|]+/); | ||
@@ -64,5 +64,3 @@ if (flags.length > 1 && !/^[[<]/.test(flags[1])) this.short = flags.shift(); | ||
Option.prototype.name = function() { | ||
return this.long | ||
.replace('--', '') | ||
.replace('no-', ''); | ||
return this.long.replace(/^--/, ''); | ||
}; | ||
@@ -79,3 +77,3 @@ | ||
Option.prototype.attributeName = function() { | ||
return camelcase(this.name()); | ||
return camelcase(this.name().replace(/^no-/, '')); | ||
}; | ||
@@ -109,66 +107,39 @@ | ||
this._name = name || ''; | ||
this._helpFlags = '-h, --help'; | ||
this._helpDescription = 'output usage information'; | ||
this._helpShortFlag = '-h'; | ||
this._helpLongFlag = '--help'; | ||
} | ||
/** | ||
* Add command `name`. | ||
* Define a command. | ||
* | ||
* The `.action()` callback is invoked when the | ||
* command `name` is specified via __ARGV__, | ||
* and the remaining arguments are applied to the | ||
* function for access. | ||
* There are two styles of command: pay attention to where to put the description. | ||
* | ||
* When the `name` is "*" an un-matched command | ||
* will be passed as the first arg, followed by | ||
* the rest of __ARGV__ remaining. | ||
* | ||
* Examples: | ||
* | ||
* // Command implemented using action handler (description is supplied separately to `.command`) | ||
* program | ||
* .version('0.0.1') | ||
* .option('-C, --chdir <path>', 'change the working directory') | ||
* .option('-c, --config <path>', 'set config path. defaults to ./deploy.conf') | ||
* .option('-T, --no-tests', 'ignore test hook') | ||
* | ||
* program | ||
* .command('setup') | ||
* .description('run remote setup commands') | ||
* .action(function() { | ||
* console.log('setup'); | ||
* .command('clone <source> [destination]') | ||
* .description('clone a repository into a newly created directory') | ||
* .action((source, destination) => { | ||
* console.log('clone command called'); | ||
* }); | ||
* | ||
* // Command implemented using separate executable file (description is second parameter to `.command`) | ||
* program | ||
* .command('exec <cmd>') | ||
* .description('run the given remote command') | ||
* .action(function(cmd) { | ||
* console.log('exec "%s"', cmd); | ||
* }); | ||
* .command('start <service>', 'start named service') | ||
* .command('stop [service]', 'stop named serice, or all if no name supplied'); | ||
* | ||
* program | ||
* .command('teardown <dir> [otherDirs...]') | ||
* .description('run teardown commands') | ||
* .action(function(dir, otherDirs) { | ||
* console.log('dir "%s"', dir); | ||
* if (otherDirs) { | ||
* otherDirs.forEach(function (oDir) { | ||
* console.log('dir "%s"', oDir); | ||
* }); | ||
* } | ||
* }); | ||
* | ||
* program | ||
* .command('*') | ||
* .description('deploy the given env') | ||
* .action(function(env) { | ||
* console.log('deploying "%s"', env); | ||
* }); | ||
* | ||
* program.parse(process.argv); | ||
* | ||
* @param {String} name | ||
* @param {String} [desc] for git-style sub-commands | ||
* @return {Command} the new command | ||
* @param {string} nameAndArgs - command name and arguments, args are `<required>` or `[optional]` and last may also be `variadic...` | ||
* @param {Object|string} [actionOptsOrExecDesc] - configuration options (for action), or description (for executable) | ||
* @param {Object} [execOpts] - configuration options (for executable) | ||
* @return {Command} returns new command for action handler, or top-level command for executable command | ||
* @api public | ||
*/ | ||
Command.prototype.command = function(name, desc, opts) { | ||
Command.prototype.command = function(nameAndArgs, actionOptsOrExecDesc, execOpts) { | ||
var desc = actionOptsOrExecDesc; | ||
var opts = execOpts; | ||
if (typeof desc === 'object' && desc !== null) { | ||
@@ -179,3 +150,3 @@ opts = desc; | ||
opts = opts || {}; | ||
var args = name.split(/ +/); | ||
var args = nameAndArgs.split(/ +/); | ||
var cmd = new Command(args.shift()); | ||
@@ -190,2 +161,7 @@ | ||
cmd._noHelp = !!opts.noHelp; | ||
cmd._helpFlags = this._helpFlags; | ||
cmd._helpDescription = this._helpDescription; | ||
cmd._helpShortFlag = this._helpShortFlag; | ||
cmd._helpLongFlag = this._helpLongFlag; | ||
cmd._executableFile = opts.executableFile; // Custom name for executable file | ||
this.commands.push(cmd); | ||
@@ -344,10 +320,13 @@ cmd.parseExpectedArgs(args); | ||
* | ||
* // simple boolean defaulting to false | ||
* // simple boolean defaulting to undefined | ||
* program.option('-p, --pepper', 'add pepper'); | ||
* | ||
* program.pepper | ||
* // => undefined | ||
* | ||
* --pepper | ||
* program.pepper | ||
* // => Boolean | ||
* // => true | ||
* | ||
* // simple boolean defaulting to true | ||
* // simple boolean defaulting to true (unless non-negated option is also defined) | ||
* program.option('-C, --no-cheese', 'remove cheese'); | ||
@@ -389,2 +368,4 @@ * | ||
if (fn instanceof RegExp) { | ||
// This is a bit simplistic (especially no error messages), and probably better handled by caller using custom option processing. | ||
// No longer documented in README, but still present for backwards compatibility. | ||
var regex = fn; | ||
@@ -401,6 +382,9 @@ fn = function(val, def) { | ||
// preassign default value only for --no-*, [optional], or <required> | ||
if (!option.bool || option.optional || option.required) { | ||
// when --no-* we make sure default is true | ||
if (!option.bool) defaultValue = true; | ||
// preassign default value for --no-*, [optional], <required>, or plain flag if boolean value | ||
if (option.negate || option.optional || option.required || typeof defaultValue === 'boolean') { | ||
// when --no-foo we make sure default is true, unless a --foo option is already defined | ||
if (option.negate) { | ||
var opts = self.opts(); | ||
defaultValue = Object.prototype.hasOwnProperty.call(opts, name) ? opts[name] : true; | ||
} | ||
// preassign only if we have a default | ||
@@ -424,9 +408,9 @@ if (defaultValue !== undefined) { | ||
// unassigned or bool | ||
// unassigned or boolean value | ||
if (typeof self[name] === 'boolean' || typeof self[name] === 'undefined') { | ||
// if no value, bool true, and we have a default, then use it! | ||
// if no value, negate false, and we have a default, then use it! | ||
if (val == null) { | ||
self[name] = option.bool | ||
? defaultValue || true | ||
: false; | ||
self[name] = option.negate | ||
? false | ||
: defaultValue || true; | ||
} else { | ||
@@ -437,3 +421,3 @@ self[name] = val; | ||
// reassign | ||
self[name] = val; | ||
self[name] = option.negate ? false : val; | ||
} | ||
@@ -478,7 +462,8 @@ }); | ||
// this user needs help | ||
argv.push('--help'); | ||
argv.push(this._helpLongFlag); | ||
} | ||
// process argv | ||
var parsed = this.parseOptions(this.normalize(argv.slice(2))); | ||
var normalized = this.normalize(argv.slice(2)); | ||
var parsed = this.parseOptions(normalized); | ||
var args = this.args = parsed.args; | ||
@@ -489,22 +474,35 @@ | ||
// executable sub-commands | ||
// (Debugging note for future: args[0] is not right if an action has been called) | ||
var name = result.args[0]; | ||
var subCommand = null; | ||
var aliasCommand = null; | ||
// check alias of sub commands | ||
// Look for subcommand | ||
if (name) { | ||
aliasCommand = this.commands.filter(function(command) { | ||
subCommand = this.commands.find(function(command) { | ||
return command._name === name; | ||
}); | ||
} | ||
// Look for alias | ||
if (!subCommand && name) { | ||
subCommand = this.commands.find(function(command) { | ||
return command.alias() === name; | ||
})[0]; | ||
}); | ||
if (subCommand) { | ||
name = subCommand._name; | ||
args[0] = name; | ||
} | ||
} | ||
// Look for default subcommand | ||
if (!subCommand && this.defaultExecutable) { | ||
name = this.defaultExecutable; | ||
args.unshift(name); | ||
subCommand = this.commands.find(function(command) { | ||
return command._name === name; | ||
}); | ||
} | ||
if (this._execs[name] && typeof this._execs[name] !== 'function') { | ||
return this.executeSubCommand(argv, args, parsed.unknown); | ||
} else if (aliasCommand) { | ||
// is alias of a subCommand | ||
args[0] = aliasCommand._name; | ||
return this.executeSubCommand(argv, args, parsed.unknown); | ||
} else if (this.defaultExecutable) { | ||
// use the default subcommand | ||
args.unshift(this.defaultExecutable); | ||
return this.executeSubCommand(argv, args, parsed.unknown); | ||
return this.executeSubCommand(argv, args, parsed.unknown, subCommand ? subCommand._executableFile : undefined); | ||
} | ||
@@ -521,6 +519,7 @@ | ||
* @param {Array} unknown | ||
* @param {String} specifySubcommand | ||
* @api private | ||
*/ | ||
Command.prototype.executeSubCommand = function(argv, args, unknown) { | ||
Command.prototype.executeSubCommand = function(argv, args, unknown, executableFile) { | ||
args = args.concat(unknown); | ||
@@ -534,9 +533,17 @@ | ||
args[0] = args[1]; | ||
args[1] = '--help'; | ||
args[1] = this._helpLongFlag; | ||
} | ||
var isExplicitJS = false; // Whether to use node to launch "executable" | ||
// executable | ||
var f = argv[1]; | ||
// name of the subcommand, link `pm-install` | ||
var bin = basename(f, path.extname(f)) + '-' + args[0]; | ||
var pm = argv[1]; | ||
// name of the subcommand, like `pm-install` | ||
var bin = basename(pm, path.extname(pm)) + '-' + args[0]; | ||
if (executableFile != null) { | ||
bin = executableFile; | ||
// Check for same extensions as we scan for below so get consistent launch behaviour. | ||
var executableExt = path.extname(executableFile); | ||
isExplicitJS = executableExt === '.js' || executableExt === '.ts' || executableExt === '.mjs'; | ||
} | ||
@@ -547,3 +554,3 @@ // In case of globally installed, get the base dir where executable | ||
var resolvedLink = fs.realpathSync(f); | ||
var resolvedLink = fs.realpathSync(pm); | ||
@@ -556,3 +563,2 @@ baseDir = dirname(resolvedLink); | ||
// whether bin file is a js script with explicit `.js` or `.ts` extension | ||
var isExplicitJS = false; | ||
if (exists(localBin + '.js')) { | ||
@@ -564,2 +570,5 @@ bin = localBin + '.js'; | ||
isExplicitJS = true; | ||
} else if (exists(localBin + '.mjs')) { | ||
bin = localBin + '.mjs'; | ||
isExplicitJS = true; | ||
} else if (exists(localBin)) { | ||
@@ -576,3 +585,3 @@ bin = localBin; | ||
// add executable arguments to spawn | ||
args = (process.execArgv || []).concat(args); | ||
args = incrementNodeInspectorPort(process.execArgv).concat(args); | ||
@@ -585,2 +594,4 @@ proc = spawn(process.argv[0], args, { stdio: 'inherit', customFds: [0, 1, 2] }); | ||
args.unshift(bin); | ||
// add executable arguments to spawn | ||
args = incrementNodeInspectorPort(process.execArgv).concat(args); | ||
proc = spawn(process.execPath, args, { stdio: 'inherit' }); | ||
@@ -625,3 +636,5 @@ } | ||
lastOpt, | ||
index; | ||
index, | ||
short, | ||
opt; | ||
@@ -640,6 +653,13 @@ for (var i = 0, len = args.length; i < len; ++i) { | ||
ret.push(arg); | ||
} else if (arg.length > 1 && arg[0] === '-' && arg[1] !== '-') { | ||
arg.slice(1).split('').forEach(function(c) { | ||
ret.push('-' + c); | ||
}); | ||
} else if (arg.length > 2 && arg[0] === '-' && arg[1] !== '-') { | ||
short = arg.slice(0, 2); | ||
opt = this.optionFor(short); | ||
if (opt && (opt.required || opt.optional)) { | ||
ret.push(short); | ||
ret.push(arg.slice(2)); | ||
} else { | ||
arg.slice(1).split('').forEach(function(c) { | ||
ret.push('-' + c); | ||
}); | ||
} | ||
} else if (/^--/.test(arg) && ~(index = arg.indexOf('='))) { | ||
@@ -762,3 +782,3 @@ ret.push(arg.slice(0, index), arg.slice(index + 1)); | ||
this.emit('option:' + option.name(), arg); | ||
// bool | ||
// flag | ||
} else { | ||
@@ -777,3 +797,3 @@ this.emit('option:' + option.name()); | ||
// If it isn't, then it'll simply be ignored | ||
if ((i + 1) < argv.length && argv[i + 1][0] !== '-') { | ||
if ((i + 1) < argv.length && (argv[i + 1][0] !== '-' || argv[i + 1] === '-')) { | ||
unknownOptions.push(argv[++i]); | ||
@@ -816,3 +836,3 @@ } | ||
Command.prototype.missingArgument = function(name) { | ||
console.error("error: missing required argument `%s'", name); | ||
console.error("error: missing required argument '%s'", name); | ||
process.exit(1); | ||
@@ -831,5 +851,5 @@ }; | ||
if (flag) { | ||
console.error("error: option `%s' argument missing, got `%s'", option.flags, flag); | ||
console.error("error: option '%s' argument missing, got '%s'", option.flags, flag); | ||
} else { | ||
console.error("error: option `%s' argument missing", option.flags); | ||
console.error("error: option '%s' argument missing", option.flags); | ||
} | ||
@@ -848,3 +868,3 @@ process.exit(1); | ||
if (this._allowUnknownOption) return; | ||
console.error("error: unknown option `%s'", flag); | ||
console.error("error: unknown option '%s'", flag); | ||
process.exit(1); | ||
@@ -861,3 +881,3 @@ }; | ||
Command.prototype.variadicArgNotLast = function(name) { | ||
console.error("error: variadic arguments must be last `%s'", name); | ||
console.error("error: variadic arguments must be last '%s'", name); | ||
process.exit(1); | ||
@@ -872,4 +892,7 @@ }; | ||
* | ||
* You can optionally supply the flags and description to override the defaults. | ||
* | ||
* @param {String} str | ||
* @param {String} [flags] | ||
* @param {String} [description] | ||
* @return {Command} for chaining | ||
@@ -879,7 +902,8 @@ * @api public | ||
Command.prototype.version = function(str, flags) { | ||
Command.prototype.version = function(str, flags, description) { | ||
if (arguments.length === 0) return this._version; | ||
this._version = str; | ||
flags = flags || '-V, --version'; | ||
var versionOption = new Option(flags, 'output the version number'); | ||
description = description || 'output the version number'; | ||
var versionOption = new Option(flags, description); | ||
this._versionOptionName = versionOption.long.substr(2) || 'version'; | ||
@@ -1018,4 +1042,5 @@ this.options.push(versionOption); | ||
options.push({ | ||
flags: '-h, --help' | ||
flags: this._helpFlags | ||
}); | ||
return options.reduce(function(max, option) { | ||
@@ -1076,4 +1101,4 @@ return Math.max(max, option.flags.length); | ||
return pad(option.flags, width) + ' ' + option.description + | ||
((option.bool && option.defaultValue !== undefined) ? ' (default: ' + JSON.stringify(option.defaultValue) + ')' : ''); | ||
}).concat([pad('-h, --help', width) + ' ' + 'output usage information']) | ||
((!option.negate && option.defaultValue !== undefined) ? ' (default: ' + JSON.stringify(option.defaultValue) + ')' : ''); | ||
}).concat([pad(this._helpFlags, width) + ' ' + this._helpDescription]) | ||
.join('\n'); | ||
@@ -1136,4 +1161,8 @@ }; | ||
} | ||
var parentCmdNames = ''; | ||
for (var parentCmd = this.parent; parentCmd; parentCmd = parentCmd.parent) { | ||
parentCmdNames = parentCmd.name() + ' ' + parentCmdNames; | ||
} | ||
var usage = [ | ||
'Usage: ' + cmdName + ' ' + this.usage(), | ||
'Usage: ' + parentCmdNames + cmdName + ' ' + this.usage(), | ||
'' | ||
@@ -1160,4 +1189,7 @@ ]; | ||
/** | ||
* Output help information for this command | ||
* Output help information for this command. | ||
* | ||
* When listener(s) are available for the helpLongFlag | ||
* those callbacks are invoked. | ||
* | ||
* @api public | ||
@@ -1172,9 +1204,37 @@ */ | ||
} | ||
process.stdout.write(cb(this.helpInformation())); | ||
this.emit('--help'); | ||
const cbOutput = cb(this.helpInformation()); | ||
if (typeof cbOutput !== 'string' && !Buffer.isBuffer(cbOutput)) { | ||
throw new Error('outputHelp callback must return a string or a Buffer'); | ||
} | ||
process.stdout.write(cbOutput); | ||
this.emit(this._helpLongFlag); | ||
}; | ||
/** | ||
* You can pass in flags and a description to override the help | ||
* flags and help description for your command. | ||
* | ||
* @param {String} [flags] | ||
* @param {String} [description] | ||
* @return {Command} | ||
* @api public | ||
*/ | ||
Command.prototype.helpOption = function(flags, description) { | ||
this._helpFlags = flags || this._helpFlags; | ||
this._helpDescription = description || this._helpDescription; | ||
var splitFlags = this._helpFlags.split(/[ ,|]+/); | ||
if (splitFlags.length > 1) this._helpShortFlag = splitFlags.shift(); | ||
this._helpLongFlag = splitFlags.shift(); | ||
return this; | ||
}; | ||
/** | ||
* Output help information and exit. | ||
* | ||
* @param {Function} [cb] | ||
* @api public | ||
@@ -1226,4 +1286,5 @@ */ | ||
options = options || []; | ||
for (var i = 0; i < options.length; i++) { | ||
if (options[i] === '--help' || options[i] === '-h') { | ||
if (options[i] === cmd._helpLongFlag || options[i] === cmd._helpShortFlag) { | ||
cmd.outputHelp(); | ||
@@ -1261,1 +1322,48 @@ process.exit(0); | ||
} | ||
/** | ||
* Scan arguments and increment port number for inspect calls (to avoid conflicts when spawning new command). | ||
* | ||
* @param {string[]} args - array of arguments from node.execArgv | ||
* @returns {string[]} | ||
* @api private | ||
*/ | ||
function incrementNodeInspectorPort(args) { | ||
// Testing for these options: | ||
// --inspect[=[host:]port] | ||
// --inspect-brk[=[host:]port] | ||
// --inspect-port=[host:]port | ||
return args.map((arg) => { | ||
var result = arg; | ||
if (arg.indexOf('--inspect') === 0) { | ||
var debugOption; | ||
var debugHost = '127.0.0.1'; | ||
var debugPort = '9229'; | ||
var match; | ||
if ((match = arg.match(/^(--inspect(-brk)?)$/)) !== null) { | ||
// e.g. --inspect | ||
debugOption = match[1]; | ||
} else if ((match = arg.match(/^(--inspect(-brk|-port)?)=([^:]+)$/)) !== null) { | ||
debugOption = match[1]; | ||
if (/^\d+$/.test(match[3])) { | ||
// e.g. --inspect=1234 | ||
debugPort = match[3]; | ||
} else { | ||
// e.g. --inspect=localhost | ||
debugHost = match[3]; | ||
} | ||
} else if ((match = arg.match(/^(--inspect(-brk|-port)?)=([^:]+):(\d+)$/)) !== null) { | ||
// e.g. --inspect=localhost:1234 | ||
debugOption = match[1]; | ||
debugHost = match[3]; | ||
debugPort = match[4]; | ||
} | ||
if (debugOption && debugPort !== '0') { | ||
result = `${debugOption}=${debugHost}:${parseInt(debugPort) + 1}`; | ||
} | ||
} | ||
return result; | ||
}); | ||
} |
{ | ||
"name": "commander", | ||
"version": "2.20.0", | ||
"version": "3.0.0-0", | ||
"description": "the complete solution for node.js command-line programs", | ||
@@ -29,11 +29,11 @@ "keywords": [ | ||
"devDependencies": { | ||
"@types/node": "^10.11.3", | ||
"eslint": "^5.6.1", | ||
"@types/node": "^12.6.2", | ||
"eslint": "^6.0.1", | ||
"should": "^13.2.3", | ||
"sinon": "^6.3.4", | ||
"standard": "^12.0.1", | ||
"ts-node": "^7.0.1", | ||
"typescript": "^2.9.2" | ||
"sinon": "^7.3.2", | ||
"standard": "^13.0.1", | ||
"ts-node": "^8.3.0", | ||
"typescript": "^3.5.3" | ||
}, | ||
"typings": "typings/index.d.ts" | ||
} |
416
Readme.md
# Commander.js | ||
[![Build Status](https://api.travis-ci.org/tj/commander.js.svg?branch=master)](http://travis-ci.org/tj/commander.js) | ||
@@ -8,8 +7,5 @@ [![NPM Version](http://img.shields.io/npm/v/commander.svg?style=flat)](https://www.npmjs.org/package/commander) | ||
[![Install Size](https://packagephobia.now.sh/badge?p=commander)](https://packagephobia.now.sh/result?p=commander) | ||
[![Join the chat at https://gitter.im/tj/commander.js](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/tj/commander.js?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) | ||
The complete solution for [node.js](http://nodejs.org) command-line interfaces, inspired by Ruby's [commander](https://github.com/commander-rb/commander). | ||
[API documentation](http://tj.github.com/commander.js/) | ||
The complete solution for [node.js](http://nodejs.org) command-line interfaces, inspired by Ruby's [commander](https://github.com/commander-rb/commander). | ||
## Installation | ||
@@ -19,168 +15,276 @@ | ||
## Option parsing | ||
## Declaring _program_ variable | ||
Options with commander are defined with the `.option()` method, also serving as documentation for the options. The example below parses args and options from `process.argv`, leaving remaining args as the `program.args` array which were not consumed by options. | ||
Commander exports a global object which is convenient for quick programs. | ||
This is used in the examples in this README for brevity. | ||
```js | ||
#!/usr/bin/env node | ||
const program = require('commander'); | ||
program.version('0.0.1'); | ||
``` | ||
/** | ||
* Module dependencies. | ||
*/ | ||
For larger programs which may use commander in multiple ways, including unit testing, it is better to create a local Command object to use. | ||
var program = require('commander'); | ||
```js | ||
const commander = require('commander'); | ||
const program = new commander.Command(); | ||
program.version('0.0.1'); | ||
``` | ||
program | ||
.version('0.1.0') | ||
.option('-p, --peppers', 'Add peppers') | ||
.option('-P, --pineapple', 'Add pineapple') | ||
.option('-b, --bbq-sauce', 'Add bbq sauce') | ||
.option('-c, --cheese [type]', 'Add the specified type of cheese [marble]', 'marble') | ||
.parse(process.argv); | ||
console.log('you ordered a pizza with:'); | ||
if (program.peppers) console.log(' - peppers'); | ||
if (program.pineapple) console.log(' - pineapple'); | ||
if (program.bbqSauce) console.log(' - bbq'); | ||
console.log(' - %s cheese', program.cheese); | ||
``` | ||
## Options | ||
Short flags may be passed as a single arg, for example `-abc` is equivalent to `-a -b -c`. Multi-word options such as "--template-engine" are camel-cased, becoming `program.templateEngine` etc. | ||
Options are defined with the `.option()` method, also serving as documentation for the options. Each option can have a short flag (single character) and a long name, separated by a comma or space. | ||
Note that multi-word options starting with `--no` prefix negate the boolean value of the following word. For example, `--no-sauce` sets the value of `program.sauce` to false. | ||
The options can be accessed as properties on the Command object. Multi-word options such as "--template-engine" are camel-cased, becoming `program.templateEngine` etc. Multiple short flags may be combined as a single arg, for example `-abc` is equivalent to `-a -b -c`. | ||
```js | ||
#!/usr/bin/env node | ||
### Common option types, boolean and value | ||
/** | ||
* Module dependencies. | ||
*/ | ||
The two most used option types are a boolean flag, and an option which takes a value (declared using angle brackets). Both are `undefined` unless specified on command line. | ||
var program = require('commander'); | ||
```js | ||
const program = require('commander'); | ||
program | ||
.option('--no-sauce', 'Remove sauce') | ||
.parse(process.argv); | ||
.option('-d, --debug', 'output extra debugging') | ||
.option('-s, --small', 'small pizza size') | ||
.option('-p, --pizza-type <type>', 'flavour of pizza'); | ||
console.log('you ordered a pizza'); | ||
if (program.sauce) console.log(' with sauce'); | ||
else console.log(' without sauce'); | ||
program.parse(process.argv); | ||
if (program.debug) console.log(program.opts()); | ||
console.log('pizza details:'); | ||
if (program.small) console.log('- small pizza size'); | ||
if (program.pizzaType) console.log(`- ${program.pizzaType}`); | ||
``` | ||
To get string arguments from options you will need to use angle brackets <> for required inputs or square brackets [] for optional inputs. | ||
```bash | ||
$ pizza-options -d | ||
{ debug: true, small: undefined, pizzaType: undefined } | ||
pizza details: | ||
$ pizza-options -p | ||
error: option '-p, --pizza-type <type>' argument missing | ||
$ pizza-options -ds -p vegetarian | ||
{ debug: true, small: true, pizzaType: 'vegetarian' } | ||
pizza details: | ||
- small pizza size | ||
- vegetarian | ||
$ pizza-options --pizza-type=cheese | ||
pizza details: | ||
- cheese | ||
``` | ||
e.g. ```.option('-m --myarg [myVar]', 'my super cool description')``` | ||
`program.parse(arguments)` processes the arguments, leaving any args not consumed by the options as the `program.args` array. | ||
Then to access the input if it was passed in. | ||
### Default option value | ||
e.g. ```var myInput = program.myarg``` | ||
You can specify a default value for an option which takes a value. | ||
**NOTE**: If you pass a argument without using brackets the example above will return true and not the value passed in. | ||
```js | ||
const program = require('commander'); | ||
program | ||
.option('-c, --cheese <type>', 'add the specified type of cheese', 'blue'); | ||
## Version option | ||
program.parse(process.argv); | ||
Calling the `version` implicitly adds the `-V` and `--version` options to the command. | ||
When either of these options is present, the command prints the version number and exits. | ||
console.log(`cheese: ${program.cheese}`); | ||
``` | ||
$ ./examples/pizza -V | ||
0.0.1 | ||
```bash | ||
$ pizza-options | ||
cheese: blue | ||
$ pizza-options --cheese stilton | ||
cheese: stilton | ||
``` | ||
If you want your program to respond to the `-v` option instead of the `-V` option, simply pass custom flags to the `version` method using the same syntax as the `option` method. | ||
### Other option types, negatable boolean and flag|value | ||
You can specify a boolean option long name with a leading `no-` to set the option value to false when used. | ||
Defined alone this also makes the option true by default. | ||
If you define `--foo` first, adding `--no-foo` does not change the default value from what it would | ||
otherwise be. You can specify a default boolean value for a boolean flag and it can be overridden on command line. | ||
```js | ||
const program = require('commander'); | ||
program | ||
.version('0.0.1', '-v, --version') | ||
.option('--no-sauce', 'Remove sauce') | ||
.option('--cheese <flavour>', 'cheese flavour', 'mozzarella') | ||
.option('--no-cheese', 'plain with no cheese') | ||
.parse(process.argv); | ||
const sauceStr = program.sauce ? 'sauce' : 'no sauce'; | ||
const cheeseStr = (program.cheese === false) ? 'no cheese' : `${program.cheese} cheese`; | ||
console.log(`You ordered a pizza with ${sauceStr} and ${cheeseStr}`); | ||
``` | ||
The version flags can be named anything, but the long option is required. | ||
```bash | ||
$ pizza-options | ||
You ordered a pizza with sauce and mozzarella cheese | ||
$ pizza-options --sauce | ||
error: unknown option '--sauce' | ||
$ pizza-options --cheese=blue | ||
You ordered a pizza with sauce and blue cheese | ||
$ pizza-options --no-sauce --no-cheese | ||
You ordered a pizza with no sauce and no cheese | ||
``` | ||
## Command-specific options | ||
You can specify an option which functions as a flag but may also take a value (declared using square brackets). | ||
You can attach options to a command. | ||
```js | ||
#!/usr/bin/env node | ||
const program = require('commander'); | ||
var program = require('commander'); | ||
program | ||
.command('rm <dir>') | ||
.option('-r, --recursive', 'Remove recursively') | ||
.action(function (dir, cmd) { | ||
console.log('remove ' + dir + (cmd.recursive ? ' recursively' : '')) | ||
}) | ||
.option('-c, --cheese [type]', 'Add cheese with optional type'); | ||
program.parse(process.argv) | ||
program.parse(process.argv); | ||
if (program.cheese === undefined) console.log('no cheese'); | ||
else if (program.cheese === true) console.log('add cheese'); | ||
else console.log(`add cheese type ${program.cheese}`); | ||
``` | ||
A command's options are validated when the command is used. Any unknown options will be reported as an error. However, if an action-based command does not define an action, then the options are not validated. | ||
```bash | ||
$ pizza-options | ||
no cheese | ||
$ pizza-options --cheese | ||
add cheese | ||
$ pizza-options --cheese mozzarella | ||
add cheese type mozzarella | ||
``` | ||
## Coercion | ||
### Custom option processing | ||
You may specify a function to do custom processing of option values. The callback function receives two parameters, the user specified value and the | ||
previous value for the option. It returns the new value for the option. | ||
This allows you to coerce the option value to the desired type, or accumulate values, or do entirely custom processing. | ||
You can optionally specify the default/starting value for the option after the function. | ||
```js | ||
function range(val) { | ||
return val.split('..').map(Number); | ||
const program = require('commander'); | ||
function myParseInt(value, dummyPrevious) { | ||
// parseInt takes a string and an optional radix | ||
return parseInt(value); | ||
} | ||
function list(val) { | ||
return val.split(','); | ||
function increaseVerbosity(dummyValue, previous) { | ||
return previous + 1; | ||
} | ||
function collect(val, memo) { | ||
memo.push(val); | ||
return memo; | ||
function collect(value, previous) { | ||
return previous.concat([value]); | ||
} | ||
function increaseVerbosity(v, total) { | ||
return total + 1; | ||
function commaSeparatedList(value, dummyPrevious) { | ||
return value.split(','); | ||
} | ||
program | ||
.version('0.1.0') | ||
.usage('[options] <file ...>') | ||
.option('-i, --integer <n>', 'An integer argument', parseInt) | ||
.option('-f, --float <n>', 'A float argument', parseFloat) | ||
.option('-r, --range <a>..<b>', 'A range', range) | ||
.option('-l, --list <items>', 'A list', list) | ||
.option('-o, --optional [value]', 'An optional value') | ||
.option('-c, --collect [value]', 'A repeatable value', collect, []) | ||
.option('-v, --verbose', 'A value that can be increased', increaseVerbosity, 0) | ||
.parse(process.argv); | ||
.option('-f, --float <number>', 'float argument', parseFloat) | ||
.option('-i, --integer <number>', 'integer argument', myParseInt) | ||
.option('-v, --verbose', 'verbosity that can be increased', increaseVerbosity, 0) | ||
.option('-c, --collect <value>', 'repeatable value', collect, []) | ||
.option('-l, --list <items>', 'comma separated list', commaSeparatedList) | ||
; | ||
console.log(' int: %j', program.integer); | ||
console.log(' float: %j', program.float); | ||
console.log(' optional: %j', program.optional); | ||
program.range = program.range || []; | ||
console.log(' range: %j..%j', program.range[0], program.range[1]); | ||
console.log(' list: %j', program.list); | ||
console.log(' collect: %j', program.collect); | ||
console.log(' verbosity: %j', program.verbose); | ||
console.log(' args: %j', program.args); | ||
program.parse(process.argv); | ||
if (program.float !== undefined) console.log(`float: ${program.float}`); | ||
if (program.integer !== undefined) console.log(`integer: ${program.integer}`); | ||
if (program.verbose > 0) console.log(`verbosity: ${program.verbose}`); | ||
if (program.collect.length > 0) console.log(program.collect); | ||
if (program.list !== undefined) console.log(program.list); | ||
``` | ||
## Regular Expression | ||
```bash | ||
$ custom -f 1e2 | ||
float: 100 | ||
$ custom --integer 2 | ||
integer: 2 | ||
$ custom -v -v -v | ||
verbose: 3 | ||
$ custom -c a -c b -c c | ||
[ 'a', 'b', 'c' ] | ||
$ custom --list x,y,z | ||
[ 'x', 'y', 'z' ] | ||
``` | ||
### Version option | ||
The optional `version` method adds handling for displaying the command version. The default option flags are `-V` and `--version`, and when present the command prints the version number and exits. | ||
```js | ||
program.version('0.0.1'); | ||
``` | ||
```bash | ||
$ ./examples/pizza -V | ||
0.0.1 | ||
``` | ||
You may change the flags and description by passing additional parameters to the `version` method, using | ||
the same syntax for flags as the `option` method. The version flags can be named anything, but a long name is required. | ||
```js | ||
program.version('0.0.1', '-v, --vers', 'output the current version'); | ||
``` | ||
## Commands | ||
You can specify (sub)commands for your top-level command using `.command`. There are two ways these can be implemented: using an action handler attached to the command, or as a separate executable file (described in more detail later). In the first parameter to `.command` you specify the command name and any command arguments. The arguments may be `<required>` or `[optional]`, and the last argument may also be `variadic...`. | ||
For example: | ||
```js | ||
// Command implemented using action handler (description is supplied separately to `.command`) | ||
// Returns new command for configuring. | ||
program | ||
.version('0.1.0') | ||
.option('-s --size <size>', 'Pizza size', /^(large|medium|small)$/i, 'medium') | ||
.option('-d --drink [drink]', 'Drink', /^(coke|pepsi|izze)$/i) | ||
.parse(process.argv); | ||
.command('clone <source> [destination]') | ||
.description('clone a repository into a newly created directory') | ||
.action((source, destination) => { | ||
console.log('clone command called'); | ||
}); | ||
console.log(' size: %j', program.size); | ||
console.log(' drink: %j', program.drink); | ||
// Command implemented using separate executable file (description is second parameter to `.command`) | ||
// Returns top-level command for adding more commands. | ||
program | ||
.command('start <service>', 'start named service') | ||
.command('stop [service]', 'stop named service, or all if no name supplied'); | ||
``` | ||
## Variadic arguments | ||
### Specify the argument syntax | ||
The last argument of a command can be variadic, and only the last argument. To make an argument variadic you have to | ||
append `...` to the argument name. Here is an example: | ||
You use `.arguments` to specify the arguments for the top-level command, and for subcommands they are included in the `.command` call. Angled brackets (e.g. `<required>`) indicate required input. Square brackets (e.g. `[optional]`) indicate optional input. | ||
```js | ||
#!/usr/bin/env node | ||
var program = require('commander'); | ||
/** | ||
* Module dependencies. | ||
*/ | ||
program | ||
.version('0.1.0') | ||
.arguments('<cmd> [env]') | ||
.action(function (cmd, env) { | ||
cmdValue = cmd; | ||
envValue = env; | ||
}); | ||
program.parse(process.argv); | ||
if (typeof cmdValue === 'undefined') { | ||
console.error('no command given!'); | ||
process.exit(1); | ||
} | ||
console.log('command:', cmdValue); | ||
console.log('environment:', envValue || "no environment given"); | ||
``` | ||
The last argument of a command can be variadic, and only the last argument. To make an argument variadic you | ||
append `...` to the argument name. For example: | ||
```js | ||
var program = require('commander'); | ||
@@ -203,33 +307,35 @@ | ||
An `Array` is used for the value of a variadic argument. This applies to `program.args` as well as the argument passed | ||
to your action as demonstrated above. | ||
The variadic argument is passed to the action handler as an array. (And this also applies to `program.args`.) | ||
## Specify the argument syntax | ||
### Action handler (sub)commands | ||
You can add options to a command that uses an action handler. | ||
The action handler gets passed a parameter for each argument you declared, and one additional argument which is the | ||
command object itself. This command argument has the values for the command-specific options added as properties. | ||
```js | ||
#!/usr/bin/env node | ||
var program = require('commander'); | ||
program | ||
.version('0.1.0') | ||
.arguments('<cmd> [env]') | ||
.action(function (cmd, env) { | ||
cmdValue = cmd; | ||
envValue = env; | ||
}); | ||
.command('rm <dir>') | ||
.option('-r, --recursive', 'Remove recursively') | ||
.action(function (dir, cmdObj) { | ||
console.log('remove ' + dir + (cmdObj.recursive ? ' recursively' : '')) | ||
}) | ||
program.parse(process.argv); | ||
if (typeof cmdValue === 'undefined') { | ||
console.error('no command given!'); | ||
process.exit(1); | ||
} | ||
console.log('command:', cmdValue); | ||
console.log('environment:', envValue || "no environment given"); | ||
program.parse(process.argv) | ||
``` | ||
Angled brackets (e.g. `<cmd>`) indicate required input. Square brackets (e.g. `[env]`) indicate optional input. | ||
## Git-style sub-commands | ||
A command's options on the command line are validated when the command is used. Any unknown options will be reported as an error. However, if an action-based command does not define an action, then the options are not validated. | ||
Configuration options can be passed with the call to `.command()`. Specifying `true` for `opts.noHelp` will remove the command from the generated help output. | ||
### Git-style executable (sub)commands | ||
When `.command()` is invoked with a description argument, this tells commander that you're going to use separate executables for sub-commands, much like `git(1)` and other popular tools. | ||
Commander will search the executables in the directory of the entry script (like `./examples/pm`) with the name `program-subcommand`, like `pm-install`, `pm-search`. | ||
You can specify a custom name with the `executableFile` configuration option. | ||
You handle the options for an executable (sub)command in the executable, and don't declare them at the top-level. | ||
```js | ||
@@ -243,2 +349,3 @@ // file: ./examples/pm | ||
.command('search [query]', 'search with optional query') | ||
.command('update', 'update installed packages', {executableFile: 'myUpdateSubCommand'}) | ||
.command('list', 'list packages installed', {isDefault: true}) | ||
@@ -248,15 +355,7 @@ .parse(process.argv); | ||
When `.command()` is invoked with a description argument, no `.action(callback)` should be called to handle sub-commands, otherwise there will be an error. This tells commander that you're going to use separate executables for sub-commands, much like `git(1)` and other popular tools. | ||
The commander will try to search the executables in the directory of the entry script (like `./examples/pm`) with the name `program-command`, like `pm-install`, `pm-search`. | ||
Configuration options can be passed with the call to `.command()`. Specifying `true` for `opts.noHelp` will remove the command from the generated help output. Specifying `true` for `opts.isDefault` will run the subcommand if no other subcommand is specified. | ||
Specifying a name with `executableFile` will override the default constructed name. | ||
Options can be passed with the call to `.command()`. Specifying `true` for `opts.noHelp` will remove the subcommand from the generated help output. Specifying `true` for `opts.isDefault` will run the subcommand if no other subcommand is specified. | ||
If the program is designed to be installed globally, make sure the executables have proper modes, like `755`. | ||
### `--harmony` | ||
You can enable `--harmony` option in two ways: | ||
* Use `#! /usr/bin/env node --harmony` in the sub-commands scripts. Note some os version don’t support this pattern. | ||
* Use the `--harmony` option when call the command, like `node --harmony examples/pm publish`. The `--harmony` option will be preserved when spawning sub-command process. | ||
## Automated --help | ||
@@ -273,3 +372,2 @@ | ||
Options: | ||
-h, --help output usage information | ||
-V, --version output the version number | ||
@@ -279,4 +377,5 @@ -p, --peppers Add peppers | ||
-b, --bbq Add bbq sauce | ||
-c, --cheese <type> Add the specified type of cheese [marble] | ||
-c, --cheese <type> Add the specified type of cheese (default: "marble") | ||
-C, --no-cheese You do not want any cheese | ||
-h, --help output usage information | ||
``` | ||
@@ -365,2 +464,11 @@ | ||
## .helpOption(flags, description) | ||
Override the default help flags and description. | ||
```js | ||
program | ||
.helpOption('-e, --HELP', 'read more information'); | ||
``` | ||
## .help(cb) | ||
@@ -387,2 +495,30 @@ | ||
## Bits and pieces | ||
### TypeScript | ||
The Commander package includes its TypeScript Definition file, but also requires the node types which you need to install yourself. e.g. | ||
```bash | ||
npm install commander | ||
npm install --save-dev @types/node | ||
``` | ||
If you use `ts-node` and git-style sub-commands written as `.ts` files, you need to call your program through node to get the sub-commands called correctly. e.g. | ||
```bash | ||
node -r ts-node/register pm.ts | ||
``` | ||
### Node options such as `--harmony` | ||
You can enable `--harmony` option in two ways: | ||
* Use `#! /usr/bin/env node --harmony` in the sub-commands scripts. (Note Windows does not support this pattern.) | ||
* Use the `--harmony` option when call the command, like `node --harmony examples/pm publish`. The `--harmony` option will be preserved when spawning sub-command process. | ||
### Node debugging | ||
If you are using the node inspector for [debugging](https://nodejs.org/en/docs/guides/debugging-getting-started/) git-style executable (sub)commands using `node -inspect` et al, | ||
the inspector port is incremented by 1 for the spawned subcommand. | ||
## Examples | ||
@@ -389,0 +525,0 @@ |
@@ -6,2 +6,4 @@ // Type definitions for commander 2.11 | ||
///<reference types="node" /> | ||
declare namespace local { | ||
@@ -40,73 +42,52 @@ | ||
/** | ||
* Set the program version to `str`. | ||
* Set the program version to `str`. | ||
* | ||
* This method auto-registers the "-V, --version" flag | ||
* which will print the version number when passed. | ||
* | ||
* You can optionally supply the flags and description to override the defaults. | ||
* | ||
* @param {string} str | ||
* @param {string} [flags] | ||
* @returns {Command} for chaining | ||
*/ | ||
version(str: string, flags?: string): Command; | ||
version(str: string, flags?: string, description?: string): Command; | ||
/** | ||
* Add command `name`. | ||
* | ||
* The `.action()` callback is invoked when the | ||
* command `name` is specified via __ARGV__, | ||
* and the remaining arguments are applied to the | ||
* function for access. | ||
* | ||
* When the `name` is "*" an un-matched command | ||
* will be passed as the first arg, followed by | ||
* the rest of __ARGV__ remaining. | ||
* | ||
* Define a command, implemented using an action handler. | ||
* | ||
* @remarks | ||
* The command description is supplied using `.description`, not as a parameter to `.command`. | ||
* | ||
* @example | ||
* program | ||
* .version('0.0.1') | ||
* .option('-C, --chdir <path>', 'change the working directory') | ||
* .option('-c, --config <path>', 'set config path. defaults to ./deploy.conf') | ||
* .option('-T, --no-tests', 'ignore test hook') | ||
* | ||
* program | ||
* .command('setup') | ||
* .description('run remote setup commands') | ||
* .action(function() { | ||
* console.log('setup'); | ||
* }); | ||
* | ||
* program | ||
* .command('exec <cmd>') | ||
* .description('run the given remote command') | ||
* .action(function(cmd) { | ||
* console.log('exec "%s"', cmd); | ||
* }); | ||
* | ||
* program | ||
* .command('teardown <dir> [otherDirs...]') | ||
* .description('run teardown commands') | ||
* .action(function(dir, otherDirs) { | ||
* console.log('dir "%s"', dir); | ||
* if (otherDirs) { | ||
* otherDirs.forEach(function (oDir) { | ||
* console.log('dir "%s"', oDir); | ||
* }); | ||
* } | ||
* }); | ||
* | ||
* program | ||
* .command('*') | ||
* .description('deploy the given env') | ||
* .action(function(env) { | ||
* console.log('deploying "%s"', env); | ||
* }); | ||
* | ||
* program.parse(process.argv); | ||
* | ||
* @param {string} name | ||
* @param {string} [desc] for git-style sub-commands | ||
* @param {CommandOptions} [opts] command options | ||
* @returns {Command} the new command | ||
* ```ts | ||
* program | ||
* .command('clone <source> [destination]') | ||
* .description('clone a repository into a newly created directory') | ||
* .action((source, destination) => { | ||
* console.log('clone command called'); | ||
* }); | ||
* ``` | ||
* | ||
* @param nameAndArgs - command name and arguments, args are `<required>` or `[optional]` and last may also be `variadic...` | ||
* @param opts - configuration options | ||
* @returns new command | ||
*/ | ||
command(name: string, desc?: string, opts?: commander.CommandOptions): Command; | ||
command(nameAndArgs: string, opts?: commander.CommandOptions): Command; | ||
/** | ||
* Define a command, implemented in a separate executable file. | ||
* | ||
* @remarks | ||
* The command description is supplied as the second parameter to `.command`. | ||
* | ||
* @example | ||
* ```ts | ||
* program | ||
* .command('start <service>', 'start named service') | ||
* .command('stop [service]', 'stop named serice, or all if no name supplied'); | ||
* ``` | ||
* | ||
* @param nameAndArgs - command name and arguments, args are `<required>` or `[optional]` and last may also be `variadic...` | ||
* @param description - description of executable command | ||
* @param opts - configuration options | ||
* @returns top level command for chaining more command definitions | ||
*/ | ||
command(nameAndArgs: string, description: string, opts?: commander.CommandOptions): Command; | ||
@@ -273,2 +254,5 @@ /** | ||
* | ||
* When listener(s) are available for the helpLongFlag | ||
* those callbacks are invoked. | ||
* | ||
* @param {(str: string) => string} [cb] | ||
@@ -278,6 +262,11 @@ */ | ||
/** Output help information and exit. | ||
* | ||
* @param {(str: string) => string} [cb] | ||
/** | ||
* You can pass in flags and a description to override the help | ||
* flags and help description for your command. | ||
*/ | ||
helpOption(flags?: string, description?: string): Command; | ||
/** | ||
* Output help information and exit. | ||
*/ | ||
help(cb?: (str: string) => string): never; | ||
@@ -284,0 +273,0 @@ } |
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
73303
1415
565
1