Commander.js
Commander is a light-weight, expressive, and powerful command-line framework for node.js.
exports.Option
Expose Option
.
Source
exports.Option = Option;
Option()
Initialize a new Option
with the given flags
and description
.
Source
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 || '';
}
Command()
Initialize a new Command
.
Source
function Command(name) {
this.commands = [];
this.options = [];
this._execs = {};
this._allowUnknownOption = false;
this._args = [];
this._name = name || '';
}
Command#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.
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);
Source
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;
};
Command#arguments()
Define argument syntax for the top-level command.
Source
Command.prototype.arguments = function (desc) {
return this.parseExpectedArgs(desc.split(/ +/));
};
Command#parseExpectedArgs()
Parse expected args
.
For example ["[type]"]
becomes [{ required: false, name: 'type' }]
.
Source
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;
};
Command#action()
Register callback fn
for the command.
Examples:
program
.command('help')
.description('display verbose help')
.action(function() {
// output help here
});
Source
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;
};
Command#option()
Define option with flags
, description
and optional
coercion fn
.
The flags
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 false
program.option('-p, --pepper', 'add pepper');
--pepper
program.pepper
// => Boolean
// simple boolean defaulting to true
program.option('-C, --no-cheese', 'remove cheese');
program.cheese
// => true
--no-cheese
program.cheese
// => false
// required argument
program.option('-C, --chdir <path>', 'change the working directory');
--chdir /tmp
program.chdir
// => "/tmp"
// optional argument
program.option('-c, --cheese [type]', 'add cheese [marble]');
Source
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;
};
Command#allowUnknownOption()
Allow unknown options on the command line.
Source
Command.prototype.allowUnknownOption = function(arg) {
this._allowUnknownOption = arguments.length === 0 || arg;
return this;
};
Command#parse()
Parse argv
, settings options and invoking commands when defined.
Source
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;
};
Command#parseOptions()
Parse options from argv
returning argv
void of these options.
Source
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 };
};
Command#opts()
Return an object containing options as key-value pairs
Source
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;
};
Command#description()
Set the description to str
.
Source
Command.prototype.description = function(str) {
if (0 === arguments.length) return this._description;
this._description = str;
return this;
};
Command#alias()
Set an alias for the command
Source
Command.prototype.alias = function(alias) {
if (0 == arguments.length) return this._alias;
this._alias = alias;
return this;
};
Command#usage()
Set / get the command usage str
.
Source
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;
};
Command#name()
Get the name of the command
Source
Command.prototype.name = function() {
return this._name;
};
Command#outputHelp()
Output help information for this command
Source
Command.prototype.outputHelp = function(cb) {
if (!cb) {
cb = function(passthru) {
return passthru;
}
}
process.stdout.write(cb(this.helpInformation()));
this.emit('--help');
};
Command#help()
Output help information and exit.
Source
Command.prototype.help = function(cb) {
this.outputHelp(cb);
process.exit();
};
exports.Command
Expose
Command
.Source