Skip to main content

commanderjs

· 10 min read
XOne
webmaster

A complete solution for node.js command-line interfaces, inspired by Ruby's commander.

API Documentation

Installation

$ npm install commander

Declare program variable

Commander exports a global object for easy and fast programming. For brevity, examples in this README use it.

const program = require('commander');
program.version('0.0.1');

For larger programs that may use commander in many ways, including unit testing, it is better to create a local Command object to use.

const commander = require('commander');
const program = new commander.Command();
program.version('0.0.1');

Options

The .option() method is used to define commander with options, and also for documenting these options. Each option can have a short flag (single character) and a long name, separated by a comma or space.

Options are placed on the Commander object's properties, multi-word options like --template-engine are converted to camelCase program.templateEngine. Multiple short flags can be combined into one argument, such as -a -b -c is equivalent to -abc.

Common option types, boolean and value

The two most common types of options are boolean (no value after the option) and options with a value (declared with angle brackets). Both are undefined unless specified in the command line.

const program = require('commander');

program
.option('-d, --debug', 'output extra debugging')
.option('-s, --small', 'small pizza size')
.option('-p, --pizza-type <type>', 'flavour of pizza');

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}`);
$ 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

program.parse(arguments)processes the arguments, unused options are stored in the program.argsarray.

Default option values

You can set a default value for an option.

const program = require('commander');

program.option('-c, --cheese <type>', 'add the specified type of cheese', 'blue');

program.parse(process.argv);

console.log(`cheese: ${program.cheese}`);
$ pizza-options
cheese: blue
$ pizza-options --cheese stilton
cheese: stilton

Other option types, ignorable booleans and flag values

When the option value is of type boolean, you can add no- to the long name to make the default value true, and if the option is passed, the value is false.

const program = require('commander');

program.option('-n, --no-sauce', 'Remove sauce').parse(process.argv);

if (program.sauce) console.log('you ordered a pizza with sauce');
else console.log('you ordered a pizza without sauce');
$ pizza-options
you ordered a pizza with sauce
$ pizza-options --sauce
error: unknown option `--sauce'
$ pizza-options --no-sauce
you ordered a pizza without sauce

You can specify an option as a flag that accepts a value (declared with square brackets, i.e., passing a value is not required).

const program = require('commander');

program.option('-c, --cheese [type]', 'Add cheese with optional type');

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}`);

Custom option processing

You can specify a function to process the value of an option, which takes two arguments: the value passed by the user and the previous value, and it returns a new option value.

You can cast the option value to the required type, accumulate values, or completely customize the processing.

You can specify the default or initial value of the option after the function.

const program = require('commander');

function myParseInt(value, dummyPrevious) {
// parseInt takes a string and an optional radix
return parseInt(value);
}

function increaseVerbosity(dummyValue, previous) {
return previous + 1;
}

function collect(value, previous) {
return previous.concat([value]);
}

function commaSeparatedList(value, dummyPrevious) {
return value.split(',');
}

program
.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);

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);
$ 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 versionmethod processes the display version command, the default option flags are -V and --version, and the version number is printed and exited when present.

program.version('0.0.1');
    $ ./examples/pizza -V
0.0.1

You can customize the flags by passing another argument to the version method, with the syntax similar to the option method. The version flag name can be arbitrary, but it must have a long name.

program.version('0.0.1', '-v, --version');

Specify command options

You can bind options to commands.

#!/usr/bin/env node

var program = require('commander');

program
.command('rm <dir>')
.option('-r, --recursive', 'Remove recursively')
.action(function (dir, cmd) {
console.log('remove ' + dir + (cmd.recursive ? ' recursively' : ''));
});

program.parse(process.argv);

When using this command, the options of the command will be verified. Any unknown options will be reported as an error. However, if the command based on the operation does not define an action, the options are not verified.

Regular expressions

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);

console.log(' size: %j', program.size);
console.log(' drink: %j', program.drink);

Note: If the value passed for the size option does not match the regular expression, the value is medium (default value). If the drink option does not match the regular expression, the value is true.

Variable number of arguments

The last argument of a command can be a variable number of arguments, and only the last argument can be variable. To make an argument variable, you need to append ... after the argument name. Here is an example:

#!/usr/bin/env node

/**
* Module dependencies.
*/

var program = require('commander');

program
.version('0.0.1')
.command('rmdir <dir> [otherDirs...]')
.action(function (dir, otherDirs) {
console.log('rmdir %s', dir);
if (otherDirs) {
otherDirs.forEach(function (oDir) {
console.log('rmdir %s', oDir);
});
}
});

program.parse(process.argv);

The values of variable arguments are saved in the form of an array. As shown above, this is true for the arguments passed to your action as well as the values in program.args.

Syntax for specifying arguments

#!/usr/bin/env node

var program = require('../');

program
.version('0.0.1')
.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');

Angle brackets (e.g., <cmd>) represent required inputs, and square brackets (e.g., [env]) represent optional inputs.

Git-style subcommands

// file: ./examples/pm
var program = require('..');

program
.version('0.0.1')
.command('install [name]', 'install one or more packages')
.command('search [query]', 'search with optional query')
.command('list', 'list packages installed', { isDefault: true })
.parse(process.argv);

When .command() has a description argument, you cannot use .action(callback) to handle subcommands, otherwise, it will cause an error. This tells commander that you will use separate executable files as subcommands, just like git(1) and other popular tools. Commander will attempt to search for program-command style executable files in the directory of the entry script (e.g., ./examples/pm), such as pm-install, pm-search.

You can pass options when calling .command(). Setting opts.noHelp to true will exclude the option from the generated help output. Setting opts.isDefault to true will execute the subcommand when no other subcommand is specified.

If you plan to install the command globally, make sure the executable files have the corresponding permissions, such as 755.

--harmony

You can enable --harmony in two ways:

  • Add #!/usr/bin/env node --harmony to the subcommand script. Note that some system versions do not support this mode.
  • Add the --harmony argument when calling the command, for example, node --harmony examples/pm publish --harmony the option will be retained when spawning child processes.

Automated help information --help

Help information is automatically generated by commander based on your program, and the following is the help information generated by --help

$ ./examples/pizza --help
Usage: pizza [options]

An application for pizzas ordering

Options:
-h, --help output usage information
-V, --version output the version number
-p, --peppers Add peppers
-P, --pineapple Add pineapple
-b, --bbq Add bbq sauce
-c, --cheese <type> Add the specified type of cheese [marble]
-C, --no-cheese You do not want any cheese

Custom help

You can control what -h, --help displays by listening to --help. Once invoked, Commander will automatically exit, and the rest of your program will not be displayed. For example, the "stuff" below will not be output when --help is executed.

#!/usr/bin/env node

/**
* Module dependencies.
*/

var program = require('commander');

program.version('0.0.1').option('-f, --foo', 'enable some foo').option('-b, --bar', 'enable some bar').option('-B, --baz', 'enable some baz');

// must be before .parse() since
// node's emit() is immediate

program.on('--help', function () {
console.log('');
console.log('Examples:');
console.log(' $ custom-help --help');
console.log(' $ custom-help -h');
});

program.parse(process.argv);

console.log('stuff');

The following help information is output when running node script-name.js -h or node script-name.js --help:

Usage: custom-help [options]

Options:
-h, --help output usage information
-V, --version output the version number
-f, --foo enable some foo
-b, --bar enable some bar
-B, --baz enable some baz

Examples:
$ custom-help --help
$ custom-help -h

.outputHelp(cb)

Outputs help information without exiting. An optional callback can be processed after displaying the help text. If you want to display the default help (for example, if no command is provided), you can use something like this:

var program = require('commander');
var colors = require('colors');

program.version('0.0.1').command('getstream [url]', 'get stream URL').parse(process.argv);

if (!process.argv.slice(2).length) {
program.outputHelp(make_red);
}

function make_red(txt) {
return colors.red(txt); // 在控制台上显示红色的帮助文本
}

.help(cb)

Outputs help information and exits immediately. An optional callback can be processed after displaying the help text.

Custom event listening

You can perform custom functions by listening to commands and options.

// 当有选项verbose时会执行函数
program.on('option:verbose', function () {
process.env.VERBOSE = this.verbose;
});

// 未知命令会报错
program.on('command:*', function () {
console.error('Invalid command: %s\nSee --help for a list of available commands.', program.args.join(' '));
process.exit(1);
});

Miscellaneous knowledge

TypeScript

The package includes TypeScript definition files, but you need to install node types yourself. For example:

npm install commander
npm install --save-dev @types/node

If you are using ts-node and git-style subcommands to write .ts files, you need to execute the program with node to ensure that subcommands are executed correctly. For example:

node -r ts-node/register pm.ts

Examples

var program = require('commander');

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 [env]')
.description('run setup commands for all envs')
.option('-s, --setup_mode [mode]', 'Which setup mode to use')
.action(function (env, options) {
var mode = options.setup_mode || 'normal';
env = env || 'all';
console.log('setup for %s env(s) with %s mode', env, mode);
});

program
.command('exec <cmd>')
.alias('ex')
.description('execute the given remote cmd')
.option('-e, --exec_mode <mode>', 'Which exec mode to use')
.action(function (cmd, options) {
console.log('exec "%s" using %s mode', cmd, options.exec_mode);
})
.on('--help', function () {
console.log('');
console.log('Examples:');
console.log('');
console.log(' $ deploy exec sequential');
console.log(' $ deploy exec async');
});

program.command('*').action(function (env) {
console.log('deploying "%s"', env);
});

program.parse(process.argv);