aboutsummaryrefslogtreecommitdiff
path: root/node_modules/commander/index.js
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/commander/index.js')
-rw-r--r--node_modules/commander/index.js462
1 files changed, 348 insertions, 114 deletions
diff --git a/node_modules/commander/index.js b/node_modules/commander/index.js
index 8378d19a2..a19c05d2e 100644
--- a/node_modules/commander/index.js
+++ b/node_modules/commander/index.js
@@ -1,19 +1,20 @@
-
/**
* Module dependencies.
*/
var EventEmitter = require('events').EventEmitter;
var spawn = require('child_process').spawn;
+var readlink = require('graceful-readlink').readlinkSync;
var path = require('path');
var dirname = path.dirname;
var basename = path.basename;
+var fs = require('fs');
/**
* Expose the root command.
*/
-exports = module.exports = new Command;
+exports = module.exports = new Command();
/**
* Expose `Command`.
@@ -53,7 +54,7 @@ function Option(flags, description) {
* @api private
*/
-Option.prototype.name = function(){
+Option.prototype.name = function() {
return this.long
.replace('--', '')
.replace('no-', '');
@@ -67,9 +68,8 @@ Option.prototype.name = function(){
* @api private
*/
-Option.prototype.is = function(arg){
- return arg == this.short
- || arg == this.long;
+Option.prototype.is = function(arg) {
+ return arg == this.short || arg == this.long;
};
/**
@@ -82,9 +82,10 @@ Option.prototype.is = function(arg){
function Command(name) {
this.commands = [];
this.options = [];
- this._execs = [];
+ this._execs = {};
+ this._allowUnknownOption = false;
this._args = [];
- this._name = name;
+ this._name = name || '';
}
/**
@@ -116,46 +117,76 @@ Command.prototype.__proto__ = EventEmitter.prototype;
* program
* .command('setup')
* .description('run remote setup commands')
- * .action(function(){
+ * .action(function() {
* console.log('setup');
* });
*
* program
* .command('exec <cmd>')
* .description('run the given remote command')
- * .action(function(cmd){
+ * .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){
+ * .action(function(env) {
* console.log('deploying "%s"', env);
* });
*
* program.parse(process.argv);
*
* @param {String} name
- * @param {String} [desc]
+ * @param {String} [desc] for git-style sub-commands
* @return {Command} the new command
* @api public
*/
-Command.prototype.command = function(name, desc) {
+Command.prototype.command = function(name, desc, opts) {
+ opts = opts || {};
var args = name.split(/ +/);
var cmd = new Command(args.shift());
- if (desc) cmd.description(desc);
- if (desc) this.executables = true;
- if (desc) this._execs[cmd._name] = true;
+
+ 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.
+ *
+ * @api public
+ */
+
+Command.prototype.arguments = function (desc) {
+ return this.parseExpectedArgs(desc.split(/ +/));
+};
+
+/**
* Add an implicit `help [cmd]` subcommand
* which invokes `--help` for the given command.
*
@@ -176,18 +207,33 @@ Command.prototype.addImplicitHelpCommand = function() {
* @api public
*/
-Command.prototype.parseExpectedArgs = function(args){
+Command.prototype.parseExpectedArgs = function(args) {
if (!args.length) return;
var self = this;
- args.forEach(function(arg){
+ args.forEach(function(arg) {
+ var argDetails = {
+ required: false,
+ name: '',
+ variadic: false
+ };
+
switch (arg[0]) {
case '<':
- self._args.push({ required: true, name: arg.slice(1, -1) });
+ argDetails.required = true;
+ argDetails.name = arg.slice(1, -1);
break;
case '[':
- self._args.push({ required: false, name: arg.slice(1, -1) });
+ 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;
};
@@ -200,7 +246,7 @@ Command.prototype.parseExpectedArgs = function(args){
* program
* .command('help')
* .description('display verbose help')
- * .action(function(){
+ * .action(function() {
* // output help here
* });
*
@@ -209,9 +255,9 @@ Command.prototype.parseExpectedArgs = function(args){
* @api public
*/
-Command.prototype.action = function(fn){
+Command.prototype.action = function(fn) {
var self = this;
- var listener = function(args, unknown){
+ var listener = function(args, unknown) {
// Parse any so-far unknown options
args = args || [];
unknown = unknown || [];
@@ -231,9 +277,15 @@ Command.prototype.action = function(fn){
// 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){
+ 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);
}
});
@@ -246,10 +298,12 @@ Command.prototype.action = function(fn){
args.push(self);
}
- fn.apply(this, args);
+ fn.apply(self, args);
};
- this.parent.on(this._name, listener);
- if (this._alias) this.parent.on(this._alias, listener);
+ 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;
};
@@ -302,14 +356,26 @@ Command.prototype.action = function(fn){
* @api public
*/
-Command.prototype.option = function(flags, description, fn, defaultValue){
+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 ('function' != typeof fn) defaultValue = fn, fn = null;
+ 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 <required>
if (false == option.bool || option.optional || option.required) {
@@ -324,9 +390,11 @@ Command.prototype.option = function(flags, description, fn, defaultValue){
// when it's passed assign the value
// and conditionally invoke the callback
- this.on(oname, function(val){
+ this.on(oname, function(val) {
// coercion
- if (null !== val && fn) val = fn(val, undefined === self[name] ? defaultValue : self[name]);
+ 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]) {
@@ -348,6 +416,18 @@ Command.prototype.option = function(flags, description, fn, defaultValue){
};
/**
+ * Allow unknown options on the command line.
+ *
+ * @param {Boolean} arg if `true` or omitted, no error will be thrown
+ * for unknown options.
+ * @api public
+ */
+Command.prototype.allowUnknownOption = function(arg) {
+ this._allowUnknownOption = arguments.length === 0 || arg;
+ return this;
+};
+
+/**
* Parse `argv`, settings options and invoking commands when defined.
*
* @param {Array} argv
@@ -355,7 +435,7 @@ Command.prototype.option = function(flags, description, fn, defaultValue){
* @api public
*/
-Command.prototype.parse = function(argv){
+Command.prototype.parse = function(argv) {
// implicit help
if (this.executables) this.addImplicitHelpCommand();
@@ -365,6 +445,12 @@ Command.prototype.parse = function(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;
@@ -373,7 +459,13 @@ Command.prototype.parse = function(argv){
// executable sub-commands
var name = result.args[0];
- if (this._execs[name]) return this.executeSubCommand(argv, args, parsed.unknown);
+ 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;
};
@@ -400,24 +492,63 @@ Command.prototype.executeSubCommand = function(argv, args, unknown) {
}
// executable
- var dir = dirname(argv[1]);
- var bin = basename(argv[1], '.js') + '-' + args[0];
+ var f = argv[1];
+ // name of the subcommand, link `pm-install`
+ var bin = basename(f, '.js') + '-' + args[0];
- // check for ./<bin> first
- var local = path.join(dir, bin);
- // run it
+ // In case of globally installed, get the base dir where executable
+ // subcommand file should be located at
+ var baseDir
+ , link = readlink(f);
+
+ // when symbolink is relative path
+ if (link !== f && link.charAt(0) !== '/') {
+ link = path.join(dirname(f), link)
+ }
+ baseDir = dirname(link);
+
+ // prefer local `./<bin>` to bin in the $PATH
+ var localBin = path.join(baseDir, bin);
+
+ // whether bin file is a js script with explicit `.js` extension
+ var isExplicitJS = false;
+ if (exists(localBin + '.js')) {
+ bin = localBin + '.js';
+ isExplicitJS = true;
+ } else if (exists(localBin)) {
+ bin = localBin;
+ }
+
args = args.slice(1);
- args.unshift(local);
- var proc = spawn('node', args, { stdio: 'inherit', customFds: [0, 1, 2] });
- proc.on('error', function(err){
+
+ var proc;
+ if (process.platform !== 'win32') {
+ if (isExplicitJS) {
+ args.unshift(localBin);
+ // add executable arguments to spawn
+ args = (process.execArgv || []).concat(args);
+
+ proc = spawn('node', args, { stdio: 'inherit', customFds: [0, 1, 2] });
+ } else {
+ proc = spawn(bin, args, { stdio: 'inherit', customFds: [0, 1, 2] });
+ }
+ } else {
+ args.unshift(localBin);
+ proc = spawn(process.execPath, args, { stdio: 'inherit'});
+ }
+
+ proc.on('close', process.exit.bind(process));
+ proc.on('error', function(err) {
if (err.code == "ENOENT") {
console.error('\n %s(1) does not exist, try --help\n', bin);
} else if (err.code == "EACCES") {
console.error('\n %s(1) not executable. try chmod or run with root\n', bin);
}
+ process.exit(1);
});
+ // Store the reference to the child process
this.runningCommand = proc;
};
@@ -431,7 +562,7 @@ Command.prototype.executeSubCommand = function(argv, args, unknown) {
* @api private
*/
-Command.prototype.normalize = function(args){
+Command.prototype.normalize = function(args) {
var ret = []
, arg
, lastOpt
@@ -439,12 +570,18 @@ Command.prototype.normalize = function(args){
for (var i = 0, len = args.length; i < len; ++i) {
arg = args[i];
- i > 0 && (lastOpt = this.optionFor(args[i-1]));
+ if (i > 0) {
+ lastOpt = this.optionFor(args[i-1]);
+ }
- if (lastOpt && lastOpt.required) {
- ret.push(arg);
+ if (arg === '--') {
+ // Honor option terminator
+ ret = ret.concat(args.slice(i));
+ break;
+ } else if (lastOpt && lastOpt.required) {
+ ret.push(arg);
} else if (arg.length > 1 && '-' == arg[0] && '-' != arg[1]) {
- arg.slice(1).split('').forEach(function(c){
+ arg.slice(1).split('').forEach(function(c) {
ret.push('-' + c);
});
} else if (/^--/.test(arg) && ~(index = arg.indexOf('='))) {
@@ -469,10 +606,8 @@ Command.prototype.normalize = function(args){
* @api private
*/
-Command.prototype.parseArgs = function(args, unknown){
- var cmds = this.commands
- , len = cmds.length
- , name;
+Command.prototype.parseArgs = function(args, unknown) {
+ var name;
if (args.length) {
name = args[0];
@@ -502,7 +637,7 @@ Command.prototype.parseArgs = function(args, unknown){
* @api private
*/
-Command.prototype.optionFor = function(arg){
+Command.prototype.optionFor = function(arg) {
for (var i = 0, len = this.options.length; i < len; ++i) {
if (this.options[i].is(arg)) {
return this.options[i];
@@ -519,7 +654,7 @@ Command.prototype.optionFor = function(arg){
* @api public
*/
-Command.prototype.parseOptions = function(argv){
+Command.prototype.parseOptions = function(argv) {
var args = []
, len = argv.length
, literal
@@ -590,13 +725,30 @@ Command.prototype.parseOptions = function(argv){
};
/**
+ * Return an object containing options as key-value pairs
+ *
+ * @return {Object}
+ * @api public
+ */
+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;
+};
+
+/**
* Argument `name` is missing.
*
* @param {String} name
* @api private
*/
-Command.prototype.missingArgument = function(name){
+Command.prototype.missingArgument = function(name) {
console.error();
console.error(" error: missing required argument `%s'", name);
console.error();
@@ -611,7 +763,7 @@ Command.prototype.missingArgument = function(name){
* @api private
*/
-Command.prototype.optionMissingArgument = function(option, flag){
+Command.prototype.optionMissingArgument = function(option, flag) {
console.error();
if (flag) {
console.error(" error: option `%s' argument missing, got `%s'", option.flags, flag);
@@ -629,13 +781,27 @@ Command.prototype.optionMissingArgument = function(option, flag){
* @api private
*/
-Command.prototype.unknownOption = function(flag){
+Command.prototype.unknownOption = function(flag) {
+ if (this._allowUnknownOption) return;
console.error();
console.error(" error: unknown option `%s'", flag);
console.error();
process.exit(1);
};
+/**
+ * Variadic argument with `name` is not the last argument as required.
+ *
+ * @param {String} name
+ * @api private
+ */
+
+Command.prototype.variadicArgNotLast = function(name) {
+ console.error();
+ console.error(" error: variadic arguments must be last `%s'", name);
+ console.error();
+ process.exit(1);
+};
/**
* Set the program version to `str`.
@@ -649,28 +815,28 @@ Command.prototype.unknownOption = function(flag){
* @api public
*/
-Command.prototype.version = function(str, flags){
+Command.prototype.version = function(str, flags) {
if (0 == arguments.length) return this._version;
this._version = str;
flags = flags || '-V, --version';
this.option(flags, 'output the version number');
- this.on('version', function(){
- console.log(str);
+ this.on('version', function() {
+ process.stdout.write(str + '\n');
process.exit(0);
});
return this;
};
/**
- * Set the description `str`.
+ * Set the description to `str`.
*
* @param {String} str
* @return {String|Command}
* @api public
*/
-Command.prototype.description = function(str){
- if (0 == arguments.length) return this._description;
+Command.prototype.description = function(str) {
+ if (0 === arguments.length) return this._description;
this._description = str;
return this;
};
@@ -683,7 +849,7 @@ Command.prototype.description = function(str){
* @api public
*/
-Command.prototype.alias = function(alias){
+Command.prototype.alias = function(alias) {
if (0 == arguments.length) return this._alias;
this._alias = alias;
return this;
@@ -697,17 +863,14 @@ Command.prototype.alias = function(alias){
* @api public
*/
-Command.prototype.usage = function(str){
- var args = this._args.map(function(arg){
- return arg.required
- ? '<' + arg.name + '>'
- : '[' + arg.name + ']';
+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 : '');
+ var usage = '[options]'
+ + (this.commands.length ? ' [command]' : '')
+ + (this._args.length ? ' ' + args.join(' ') : '');
if (0 == arguments.length) return this._usage || usage;
this._usage = str;
@@ -716,14 +879,26 @@ Command.prototype.usage = function(str){
};
/**
+ * Get the name of the command
+ *
+ * @param {String} name
+ * @return {String|Command}
+ * @api public
+ */
+
+Command.prototype.name = function() {
+ return this._name;
+};
+
+/**
* Return the largest option length.
*
* @return {Number}
* @api private
*/
-Command.prototype.largestOptionLength = function(){
- return this.options.reduce(function(max, option){
+Command.prototype.largestOptionLength = function() {
+ return this.options.reduce(function(max, option) {
return Math.max(max, option.flags.length);
}, 0);
};
@@ -735,16 +910,15 @@ Command.prototype.largestOptionLength = function(){
* @api private
*/
-Command.prototype.optionHelp = function(){
+Command.prototype.optionHelp = function() {
var width = this.largestOptionLength();
// Prepend the help information
return [pad('-h, --help', width) + ' ' + 'output usage information']
- .concat(this.options.map(function(option){
- return pad(option.flags, width)
- + ' ' + option.description;
+ .concat(this.options.map(function(option) {
+ return pad(option.flags, width) + ' ' + option.description;
}))
- .join('\n');
+ .join('\n');
};
/**
@@ -754,30 +928,36 @@ Command.prototype.optionHelp = function(){
* @api private
*/
-Command.prototype.commandHelp = function(){
+Command.prototype.commandHelp = function() {
if (!this.commands.length) return '';
+
+ var commands = this.commands.filter(function(cmd) {
+ return !cmd._noHelp;
+ }).map(function(cmd) {
+ var args = cmd._args.map(function(arg) {
+ return humanReadableArgName(arg);
+ }).join(' ');
+
+ return [
+ cmd._name
+ + (cmd._alias ? '|' + cmd._alias : '')
+ + (cmd.options.length ? ' [options]' : '')
+ + ' ' + args
+ , cmd.description()
+ ];
+ });
+
+ var width = commands.reduce(function(max, command) {
+ return Math.max(max, command[0].length);
+ }, 0);
+
return [
- ''
+ ''
, ' Commands:'
, ''
- , this.commands.map(function(cmd){
- var args = cmd._args.map(function(arg){
- return arg.required
- ? '<' + arg.name + '>'
- : '[' + arg.name + ']';
- }).join(' ');
-
- return cmd._name
- + (cmd._alias
- ? '|' + cmd._alias
- : '')
- + (cmd.options.length
- ? ' [options]'
- : '') + ' ' + args
- + (cmd.description()
- ? '\n ' + cmd.description()
- : '')
- + '\n';
+ , commands.map(function(cmd) {
+ var desc = cmd[1] ? ' ' + cmd[1] : '';
+ return pad(cmd[0], width) + desc;
}).join('\n').replace(/^/gm, ' ')
, ''
].join('\n');
@@ -790,21 +970,42 @@ Command.prototype.commandHelp = function(){
* @api private
*/
-Command.prototype.helpInformation = function(){
- return [
- ''
- , ' Usage: ' + this._name
- + (this._alias
- ? '|' + this._alias
- : '')
- + ' ' + this.usage()
- , '' + this.commandHelp()
- , ' Options:'
+Command.prototype.helpInformation = function() {
+ var desc = [];
+ if (this._description) {
+ desc = [
+ ' ' + this._description
+ , ''
+ ];
+ }
+
+ var cmdName = this._name;
+ if (this._alias) {
+ cmdName = cmdName + '|' + this._alias;
+ }
+ var usage = [
+ ''
+ ,' Usage: ' + cmdName + ' ' + this.usage()
+ , ''
+ ];
+
+ var cmds = [];
+ var commandHelp = this.commandHelp();
+ if (commandHelp) cmds = [commandHelp];
+
+ var options = [
+ ' Options:'
, ''
, '' + this.optionHelp().replace(/^/gm, ' ')
, ''
, ''
- ].join('\n');
+ ];
+
+ return usage
+ .concat(cmds)
+ .concat(desc)
+ .concat(options)
+ .join('\n');
};
/**
@@ -813,8 +1014,13 @@ Command.prototype.helpInformation = function(){
* @api public
*/
-Command.prototype.outputHelp = function(){
- process.stdout.write(this.helpInformation());
+Command.prototype.outputHelp = function(cb) {
+ if (!cb) {
+ cb = function(passthru) {
+ return passthru;
+ }
+ }
+ process.stdout.write(cb(this.helpInformation()));
this.emit('--help');
};
@@ -824,8 +1030,8 @@ Command.prototype.outputHelp = function(){
* @api public
*/
-Command.prototype.help = function(){
- this.outputHelp();
+Command.prototype.help = function(cb) {
+ this.outputHelp(cb);
process.exit();
};
@@ -838,7 +1044,7 @@ Command.prototype.help = function(){
*/
function camelcase(flag) {
- return flag.split('-').reduce(function(str, word){
+ return flag.split('-').reduce(function(str, word) {
return str + word[0].toUpperCase() + word.slice(1);
});
}
@@ -874,3 +1080,31 @@ function outputHelpIfNecessary(cmd, options) {
}
}
}
+
+/**
+ * Takes an argument an returns its human readable equivalent for help usage.
+ *
+ * @param {Object} arg
+ * @return {String}
+ * @api private
+ */
+
+function humanReadableArgName(arg) {
+ var nameOutput = arg.name + (arg.variadic === true ? '...' : '');
+
+ return arg.required
+ ? '<' + nameOutput + '>'
+ : '[' + nameOutput + ']'
+}
+
+// for versions before node v0.8 when there weren't `fs.existsSync`
+function exists(file) {
+ try {
+ if (fs.statSync(file).isFile()) {
+ return true;
+ }
+ } catch (e) {
+ return false;
+ }
+}
+