Commander is a light-weight, expressive, and powerful command-lineframework fornode.js.
ExposeCommand.
exports.Command = Command;ExposeOption.
exports.Option = Option;Initialize a newOption with the givenflags anddescription.
function Option(flags, description) { this.flags = flags; this.required = ~flags.indexOf('<'); this.optional = ~flags.indexOf('['); this.bool = !~flags.indexOf('-no-'); flags = flags.split(/[ ,|]+/); if (flags.length > 1 && !/^[[<]/.test(flags[1])) this.short = flags.shift(); this.long = flags.shift(); this.description = description || '';}Initialize a newCommand.
function Command(name) { this.commands = []; this.options = []; this._execs = {}; this._allowUnknownOption = false; this._args = []; this._name = name || '';}Add commandname.
The.action() callback is invoked when the
commandname is specified viaARGV,
and the remaining arguments are applied to the
function for access.
When thename is "*" an un-matched command
will be passed as the first arg, followed by
the rest ofARGV remaining.
Examples:
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);Command.prototype.command = function(name, desc, opts) { opts = opts || {}; var args = name.split(/ +/); var cmd = new Command(args.shift()); if (desc) { cmd.description(desc); this.executables = true; this._execs[cmd._name] = true; if (opts.isDefault) this.defaultExecutable = cmd._name; } cmd._noHelp = !!opts.noHelp; this.commands.push(cmd); cmd.parseExpectedArgs(args); cmd.parent = this; if (desc) return this; return cmd;};Define argument syntax for the top-level command.
Command.prototype.arguments = function (desc) { return this.parseExpectedArgs(desc.split(/ +/));};Parse expectedargs.
For example["[type]"] becomes[{ required: false, name: 'type' }].
Command.prototype.parseExpectedArgs = function(args) { if (!args.length) return; var self = this; args.forEach(function(arg) { var argDetails = { required: false, name: '', variadic: false }; switch (arg[0]) { case '<': argDetails.required = true; argDetails.name = arg.slice(1, -1); break; case '[': argDetails.name = arg.slice(1, -1); break; } if (argDetails.name.length > 3 && argDetails.name.slice(-3) === '...') { argDetails.variadic = true; argDetails.name = argDetails.name.slice(0, -3); } if (argDetails.name) { self._args.push(argDetails); } }); return this;};Register callbackfn for the command.
Examples:
program .command('help') .description('display verbose help') .action(function() { // output help here });Command.prototype.action = function(fn) { var self = this; var listener = function(args, unknown) { // Parse any so-far unknown options args = args || []; unknown = unknown || []; var parsed = self.parseOptions(unknown); // Output help if necessary outputHelpIfNecessary(self, parsed.unknown); // If there are still any unknown options, then we simply // die, unless someone asked for help, in which case we give it // to them, and then we die. if (parsed.unknown.length > 0) { self.unknownOption(parsed.unknown[0]); } // Leftover arguments need to be pushed back. Fixes issue #56 if (parsed.args.length) args = parsed.args.concat(args); self._args.forEach(function(arg, i) { if (arg.required && null == args[i]) { self.missingArgument(arg.name); } else if (arg.variadic) { if (i !== self._args.length - 1) { self.variadicArgNotLast(arg.name); } args[i] = args.splice(i); } }); // Always append ourselves to the end of the arguments, // to make sure we match the number of arguments the user // expects if (self._args.length) { args[self._args.length] = self; } else { args.push(self); } fn.apply(self, args); }; var parent = this.parent || this; var name = parent === this ? '*' : this._name; parent.on(name, listener); if (this._alias) parent.on(this._alias, listener); return this;};Define option withflags,description and optional
coercionfn.
Theflags string should contain both the short and long flags,
separated by comma, a pipe or space. The following are all valid
all will output this way when--help is used.
"-p, --pepper"
"-p|--pepper"
"-p --pepper"
Examples:
// simple boolean defaulting to falseprogram.option('-p, --pepper', 'add pepper');--pepperprogram.pepper// => Boolean// simple boolean defaulting to trueprogram.option('-C, --no-cheese', 'remove cheese');program.cheese// => true--no-cheeseprogram.cheese// => false// required argumentprogram.option('-C, --chdir <path>', 'change the working directory');--chdir /tmpprogram.chdir// => "/tmp"// optional argumentprogram.option('-c, --cheese [type]', 'add cheese [marble]');Command.prototype.option = function(flags, description, fn, defaultValue) { var self = this , option = new Option(flags, description) , oname = option.name() , name = camelcase(oname); // default as 3rd arg if (typeof fn != 'function') { if (fn instanceof RegExp) { var regex = fn; fn = function(val, def) { var m = regex.exec(val); return m ? m[0] : def; } } else { defaultValue = fn; fn = null; } } // preassign default value only for --no-*, [optional], or if (false == option.bool || option.optional || option.required) { // when --no-* we make sure default is true if (false == option.bool) defaultValue = true; // preassign only if we have a default if (undefined !== defaultValue) self[name] = defaultValue; } // register the option this.options.push(option); // when it's passed assign the value // and conditionally invoke the callback this.on(oname, function(val) { // coercion if (null !== val && fn) val = fn(val, undefined === self[name] ? defaultValue : self[name]); // unassigned or bool if ('boolean' == typeof self[name] || 'undefined' == typeof self[name]) { // if no value, bool true, and we have a default, then use it! if (null == val) { self[name] = option.bool ? defaultValue || true : false; } else { self[name] = val; } } else if (null !== val) { // reassign self[name] = val; } }); return this;}; Allow unknown options on the command line.
Command.prototype.allowUnknownOption = function(arg) { this._allowUnknownOption = arguments.length === 0 || arg; return this;};Parseargv, settings options and invoking commands when defined.
Command.prototype.parse = function(argv) { // implicit help if (this.executables) this.addImplicitHelpCommand(); // store raw args this.rawArgs = argv; // guess name this._name = this._name || basename(argv[1], '.js'); // github-style sub-commands with no sub-command if (this.executables && argv.length< 3 && !this.defaultExecutable) { // this user needs help argv.push('--help'); } // process argv var parsed = this.parseOptions(this.normalize(argv.slice(2))); var args = this.args = parsed.args; var result = this.parseArgs(this.args, parsed.unknown); // executable sub-commands var name = result.args[0]; if (this._execs[name] && typeof this._execs[name] != "function") { return this.executeSubCommand(argv, args, parsed.unknown); } else if (this.defaultExecutable) { // use the default subcommand args.unshift(name = this.defaultExecutable); return this.executeSubCommand(argv, args, parsed.unknown); } return result;};Parse options fromargv returningargv
void of these options.
Command.prototype.parseOptions = function(argv) { var args = [] , len = argv.length , literal , option , arg; var unknownOptions = []; // parse options for (var i = 0; i< len; ++i) { arg = argv[i]; // literal args after -- if ('--' == arg) { literal = true; continue; } if (literal) { args.push(arg); continue; } // find matching Option option = this.optionFor(arg); // option is defined if (option) { // requires arg if (option.required) { arg = argv[++i]; if (null == arg) return this.optionMissingArgument(option); this.emit(option.name(), arg); // optional arg } else if (option.optional) { arg = argv[i+1]; if (null == arg || ('-' == arg[0] && '-' != arg)) { arg = null; } else { ++i; } this.emit(option.name(), arg); // bool } else { this.emit(option.name()); } continue; } // looks like an option if (arg.length > 1 && '-' == arg[0]) { unknownOptions.push(arg); // If the next argument looks like it might be // an argument for this option, we pass it on. // If it isn't, then it'll simply be ignored if (argv[i+1] && '-' != argv[i+1][0]) { unknownOptions.push(argv[++i]); } continue; } // arg args.push(arg); } return { args: args, unknown: unknownOptions };};Return an object containing options as key-value pairs
Command.prototype.opts = function() { var result = {} , len = this.options.length; for (var i = 0 ; i< len; i++) { var key = camelcase(this.options[i].name()); result[key] = key === 'version' ? this._version : this[key]; } return result;};Set the description tostr.
Command.prototype.description = function(str) { if (0 === arguments.length) return this._description; this._description = str; return this;};Set an alias for the command
Command.prototype.alias = function(alias) { if (0 == arguments.length) return this._alias; this._alias = alias; return this;};Set / get the command usagestr.
Command.prototype.usage = function(str) { var args = this._args.map(function(arg) { return humanReadableArgName(arg); }); var usage = '[options]' + (this.commands.length ? ' [command]' : '') + (this._args.length ? ' ' + args.join(' ') : ''); if (0 == arguments.length) return this._usage || usage; this._usage = str; return this;};Get the name of the command
Command.prototype.name = function() { return this._name;};Output help information for this command
Command.prototype.outputHelp = function(cb) { if (!cb) { cb = function(passthru) { return passthru; } } process.stdout.write(cb(this.helpInformation())); this.emit('--help');};Output help information and exit.
Command.prototype.help = function(cb) { this.outputHelp(cb); process.exit();};