react / wstein / node_modules / jest-cli / node_modules / jsdom / node_modules / request / node_modules / har-validator / node_modules / commander / index.js
81152 views1/**2* Module dependencies.3*/45var EventEmitter = require('events').EventEmitter;6var spawn = require('child_process').spawn;7var readlink = require('graceful-readlink').readlinkSync;8var path = require('path');9var dirname = path.dirname;10var basename = path.basename;11var fs = require('fs');1213/**14* Expose the root command.15*/1617exports = module.exports = new Command();1819/**20* Expose `Command`.21*/2223exports.Command = Command;2425/**26* Expose `Option`.27*/2829exports.Option = Option;3031/**32* Initialize a new `Option` with the given `flags` and `description`.33*34* @param {String} flags35* @param {String} description36* @api public37*/3839function Option(flags, description) {40this.flags = flags;41this.required = ~flags.indexOf('<');42this.optional = ~flags.indexOf('[');43this.bool = !~flags.indexOf('-no-');44flags = flags.split(/[ ,|]+/);45if (flags.length > 1 && !/^[[<]/.test(flags[1])) this.short = flags.shift();46this.long = flags.shift();47this.description = description || '';48}4950/**51* Return option name.52*53* @return {String}54* @api private55*/5657Option.prototype.name = function() {58return this.long59.replace('--', '')60.replace('no-', '');61};6263/**64* Check if `arg` matches the short or long flag.65*66* @param {String} arg67* @return {Boolean}68* @api private69*/7071Option.prototype.is = function(arg) {72return arg == this.short || arg == this.long;73};7475/**76* Initialize a new `Command`.77*78* @param {String} name79* @api public80*/8182function Command(name) {83this.commands = [];84this.options = [];85this._execs = [];86this._allowUnknownOption = false;87this._args = [];88this._name = name;89}9091/**92* Inherit from `EventEmitter.prototype`.93*/9495Command.prototype.__proto__ = EventEmitter.prototype;9697/**98* Add command `name`.99*100* The `.action()` callback is invoked when the101* command `name` is specified via __ARGV__,102* and the remaining arguments are applied to the103* function for access.104*105* When the `name` is "*" an un-matched command106* will be passed as the first arg, followed by107* the rest of __ARGV__ remaining.108*109* Examples:110*111* program112* .version('0.0.1')113* .option('-C, --chdir <path>', 'change the working directory')114* .option('-c, --config <path>', 'set config path. defaults to ./deploy.conf')115* .option('-T, --no-tests', 'ignore test hook')116*117* program118* .command('setup')119* .description('run remote setup commands')120* .action(function() {121* console.log('setup');122* });123*124* program125* .command('exec <cmd>')126* .description('run the given remote command')127* .action(function(cmd) {128* console.log('exec "%s"', cmd);129* });130*131* program132* .command('teardown <dir> [otherDirs...]')133* .description('run teardown commands')134* .action(function(dir, otherDirs) {135* console.log('dir "%s"', dir);136* if (otherDirs) {137* otherDirs.forEach(function (oDir) {138* console.log('dir "%s"', oDir);139* });140* }141* });142*143* program144* .command('*')145* .description('deploy the given env')146* .action(function(env) {147* console.log('deploying "%s"', env);148* });149*150* program.parse(process.argv);151*152* @param {String} name153* @param {String} [desc] for git-style sub-commands154* @return {Command} the new command155* @api public156*/157158Command.prototype.command = function(name, desc, opts) {159opts = opts || {};160var args = name.split(/ +/);161var cmd = new Command(args.shift());162163if (desc) {164cmd.description(desc);165this.executables = true;166this._execs[cmd._name] = true;167}168169cmd._noHelp = !!opts.noHelp;170this.commands.push(cmd);171cmd.parseExpectedArgs(args);172cmd.parent = this;173174if (desc) return this;175return cmd;176};177178/**179* Define argument syntax for the top-level command.180*181* @api public182*/183184Command.prototype.arguments = function (desc) {185return this.parseExpectedArgs(desc.split(/ +/));186}187188/**189* Add an implicit `help [cmd]` subcommand190* which invokes `--help` for the given command.191*192* @api private193*/194195Command.prototype.addImplicitHelpCommand = function() {196this.command('help [cmd]', 'display help for [cmd]');197};198199/**200* Parse expected `args`.201*202* For example `["[type]"]` becomes `[{ required: false, name: 'type' }]`.203*204* @param {Array} args205* @return {Command} for chaining206* @api public207*/208209Command.prototype.parseExpectedArgs = function(args) {210if (!args.length) return;211var self = this;212args.forEach(function(arg) {213var argDetails = {214required: false,215name: '',216variadic: false217};218219switch (arg[0]) {220case '<':221argDetails.required = true;222argDetails.name = arg.slice(1, -1);223break;224case '[':225argDetails.name = arg.slice(1, -1);226break;227}228229if (argDetails.name.length > 3 && argDetails.name.slice(-3) === '...') {230argDetails.variadic = true;231argDetails.name = argDetails.name.slice(0, -3);232}233if (argDetails.name) {234self._args.push(argDetails);235}236});237return this;238};239240/**241* Register callback `fn` for the command.242*243* Examples:244*245* program246* .command('help')247* .description('display verbose help')248* .action(function() {249* // output help here250* });251*252* @param {Function} fn253* @return {Command} for chaining254* @api public255*/256257Command.prototype.action = function(fn) {258var self = this;259var listener = function(args, unknown) {260// Parse any so-far unknown options261args = args || [];262unknown = unknown || [];263264var parsed = self.parseOptions(unknown);265266// Output help if necessary267outputHelpIfNecessary(self, parsed.unknown);268269// If there are still any unknown options, then we simply270// die, unless someone asked for help, in which case we give it271// to them, and then we die.272if (parsed.unknown.length > 0) {273self.unknownOption(parsed.unknown[0]);274}275276// Leftover arguments need to be pushed back. Fixes issue #56277if (parsed.args.length) args = parsed.args.concat(args);278279self._args.forEach(function(arg, i) {280if (arg.required && null == args[i]) {281self.missingArgument(arg.name);282} else if (arg.variadic) {283if (i !== self._args.length - 1) {284self.variadicArgNotLast(arg.name);285}286287args[i] = args.splice(i);288}289});290291// Always append ourselves to the end of the arguments,292// to make sure we match the number of arguments the user293// expects294if (self._args.length) {295args[self._args.length] = self;296} else {297args.push(self);298}299300fn.apply(self, args);301};302var parent = this.parent || this;303var name = parent === this ? '*' : this._name;304parent.on(name, listener);305if (this._alias) parent.on(this._alias, listener);306return this;307};308309/**310* Define option with `flags`, `description` and optional311* coercion `fn`.312*313* The `flags` string should contain both the short and long flags,314* separated by comma, a pipe or space. The following are all valid315* all will output this way when `--help` is used.316*317* "-p, --pepper"318* "-p|--pepper"319* "-p --pepper"320*321* Examples:322*323* // simple boolean defaulting to false324* program.option('-p, --pepper', 'add pepper');325*326* --pepper327* program.pepper328* // => Boolean329*330* // simple boolean defaulting to true331* program.option('-C, --no-cheese', 'remove cheese');332*333* program.cheese334* // => true335*336* --no-cheese337* program.cheese338* // => false339*340* // required argument341* program.option('-C, --chdir <path>', 'change the working directory');342*343* --chdir /tmp344* program.chdir345* // => "/tmp"346*347* // optional argument348* program.option('-c, --cheese [type]', 'add cheese [marble]');349*350* @param {String} flags351* @param {String} description352* @param {Function|Mixed} fn or default353* @param {Mixed} defaultValue354* @return {Command} for chaining355* @api public356*/357358Command.prototype.option = function(flags, description, fn, defaultValue) {359var self = this360, option = new Option(flags, description)361, oname = option.name()362, name = camelcase(oname);363364// default as 3rd arg365if (typeof fn != 'function') {366if (fn instanceof RegExp) {367var regex = fn;368fn = function(val, def) {369var m = regex.exec(val);370return m ? m[0] : def;371}372}373else {374defaultValue = fn;375fn = null;376}377}378379// preassign default value only for --no-*, [optional], or <required>380if (false == option.bool || option.optional || option.required) {381// when --no-* we make sure default is true382if (false == option.bool) defaultValue = true;383// preassign only if we have a default384if (undefined !== defaultValue) self[name] = defaultValue;385}386387// register the option388this.options.push(option);389390// when it's passed assign the value391// and conditionally invoke the callback392this.on(oname, function(val) {393// coercion394if (null !== val && fn) val = fn(val, undefined === self[name]395? defaultValue396: self[name]);397398// unassigned or bool399if ('boolean' == typeof self[name] || 'undefined' == typeof self[name]) {400// if no value, bool true, and we have a default, then use it!401if (null == val) {402self[name] = option.bool403? defaultValue || true404: false;405} else {406self[name] = val;407}408} else if (null !== val) {409// reassign410self[name] = val;411}412});413414return this;415};416417/**418* Allow unknown options on the command line.419*420* @param {Boolean} arg if `true` or omitted, no error will be thrown421* for unknown options.422* @api public423*/424Command.prototype.allowUnknownOption = function(arg) {425this._allowUnknownOption = arguments.length === 0 || arg;426return this;427};428429/**430* Parse `argv`, settings options and invoking commands when defined.431*432* @param {Array} argv433* @return {Command} for chaining434* @api public435*/436437Command.prototype.parse = function(argv) {438// implicit help439if (this.executables) this.addImplicitHelpCommand();440441// store raw args442this.rawArgs = argv;443444// guess name445this._name = this._name || basename(argv[1], '.js');446447// github-style sub-commands with no sub-command448if (this.executables && argv.length < 3) {449// this user needs help450argv.push('--help');451}452453// process argv454var parsed = this.parseOptions(this.normalize(argv.slice(2)));455var args = this.args = parsed.args;456457var result = this.parseArgs(this.args, parsed.unknown);458459// executable sub-commands460var name = result.args[0];461if (this._execs[name] && typeof this._execs[name] != "function") {462return this.executeSubCommand(argv, args, parsed.unknown);463}464465return result;466};467468/**469* Execute a sub-command executable.470*471* @param {Array} argv472* @param {Array} args473* @param {Array} unknown474* @api private475*/476477Command.prototype.executeSubCommand = function(argv, args, unknown) {478args = args.concat(unknown);479480if (!args.length) this.help();481if ('help' == args[0] && 1 == args.length) this.help();482483// <cmd> --help484if ('help' == args[0]) {485args[0] = args[1];486args[1] = '--help';487}488489// executable490var f = argv[1];491// name of the subcommand, link `pm-install`492var bin = basename(f, '.js') + '-' + args[0];493494495// In case of globally installed, get the base dir where executable496// subcommand file should be located at497var baseDir498, link = readlink(f);499500// when symbolink is relative path501if (link !== f && link.charAt(0) !== '/') {502link = path.join(dirname(f), link)503}504baseDir = dirname(link);505506// prefer local `./<bin>` to bin in the $PATH507var localBin = path.join(baseDir, bin);508509// whether bin file is a js script with explicit `.js` extension510var isExplicitJS = false;511if (exists(localBin + '.js')) {512bin = localBin + '.js';513isExplicitJS = true;514} else if (exists(localBin)) {515bin = localBin;516}517518args = args.slice(1);519520var proc;521if (process.platform !== 'win32') {522if (isExplicitJS) {523args.unshift(localBin);524// add executable arguments to spawn525args = (process.execArgv || []).concat(args);526527proc = spawn('node', args, { stdio: 'inherit', customFds: [0, 1, 2] });528} else {529proc = spawn(bin, args, { stdio: 'inherit', customFds: [0, 1, 2] });530}531} else {532args.unshift(localBin);533proc = spawn(process.execPath, args, { stdio: 'inherit'});534}535536proc.on('close', process.exit.bind(process));537proc.on('error', function(err) {538if (err.code == "ENOENT") {539console.error('\n %s(1) does not exist, try --help\n', bin);540} else if (err.code == "EACCES") {541console.error('\n %s(1) not executable. try chmod or run with root\n', bin);542}543process.exit(1);544});545546this.runningCommand = proc;547};548549/**550* Normalize `args`, splitting joined short flags. For example551* the arg "-abc" is equivalent to "-a -b -c".552* This also normalizes equal sign and splits "--abc=def" into "--abc def".553*554* @param {Array} args555* @return {Array}556* @api private557*/558559Command.prototype.normalize = function(args) {560var ret = []561, arg562, lastOpt563, index;564565for (var i = 0, len = args.length; i < len; ++i) {566arg = args[i];567if (i > 0) {568lastOpt = this.optionFor(args[i-1]);569}570571if (arg === '--') {572// Honor option terminator573ret = ret.concat(args.slice(i));574break;575} else if (lastOpt && lastOpt.required) {576ret.push(arg);577} else if (arg.length > 1 && '-' == arg[0] && '-' != arg[1]) {578arg.slice(1).split('').forEach(function(c) {579ret.push('-' + c);580});581} else if (/^--/.test(arg) && ~(index = arg.indexOf('='))) {582ret.push(arg.slice(0, index), arg.slice(index + 1));583} else {584ret.push(arg);585}586}587588return ret;589};590591/**592* Parse command `args`.593*594* When listener(s) are available those595* callbacks are invoked, otherwise the "*"596* event is emitted and those actions are invoked.597*598* @param {Array} args599* @return {Command} for chaining600* @api private601*/602603Command.prototype.parseArgs = function(args, unknown) {604var name;605606if (args.length) {607name = args[0];608if (this.listeners(name).length) {609this.emit(args.shift(), args, unknown);610} else {611this.emit('*', args);612}613} else {614outputHelpIfNecessary(this, unknown);615616// If there were no args and we have unknown options,617// then they are extraneous and we need to error.618if (unknown.length > 0) {619this.unknownOption(unknown[0]);620}621}622623return this;624};625626/**627* Return an option matching `arg` if any.628*629* @param {String} arg630* @return {Option}631* @api private632*/633634Command.prototype.optionFor = function(arg) {635for (var i = 0, len = this.options.length; i < len; ++i) {636if (this.options[i].is(arg)) {637return this.options[i];638}639}640};641642/**643* Parse options from `argv` returning `argv`644* void of these options.645*646* @param {Array} argv647* @return {Array}648* @api public649*/650651Command.prototype.parseOptions = function(argv) {652var args = []653, len = argv.length654, literal655, option656, arg;657658var unknownOptions = [];659660// parse options661for (var i = 0; i < len; ++i) {662arg = argv[i];663664// literal args after --665if ('--' == arg) {666literal = true;667continue;668}669670if (literal) {671args.push(arg);672continue;673}674675// find matching Option676option = this.optionFor(arg);677678// option is defined679if (option) {680// requires arg681if (option.required) {682arg = argv[++i];683if (null == arg) return this.optionMissingArgument(option);684this.emit(option.name(), arg);685// optional arg686} else if (option.optional) {687arg = argv[i+1];688if (null == arg || ('-' == arg[0] && '-' != arg)) {689arg = null;690} else {691++i;692}693this.emit(option.name(), arg);694// bool695} else {696this.emit(option.name());697}698continue;699}700701// looks like an option702if (arg.length > 1 && '-' == arg[0]) {703unknownOptions.push(arg);704705// If the next argument looks like it might be706// an argument for this option, we pass it on.707// If it isn't, then it'll simply be ignored708if (argv[i+1] && '-' != argv[i+1][0]) {709unknownOptions.push(argv[++i]);710}711continue;712}713714// arg715args.push(arg);716}717718return { args: args, unknown: unknownOptions };719};720721/**722* Return an object containing options as key-value pairs723*724* @return {Object}725* @api public726*/727Command.prototype.opts = function() {728var result = {}729, len = this.options.length;730731for (var i = 0 ; i < len; i++) {732var key = camelcase(this.options[i].name());733result[key] = key === 'version' ? this._version : this[key];734}735return result;736};737738/**739* Argument `name` is missing.740*741* @param {String} name742* @api private743*/744745Command.prototype.missingArgument = function(name) {746console.error();747console.error(" error: missing required argument `%s'", name);748console.error();749process.exit(1);750};751752/**753* `Option` is missing an argument, but received `flag` or nothing.754*755* @param {String} option756* @param {String} flag757* @api private758*/759760Command.prototype.optionMissingArgument = function(option, flag) {761console.error();762if (flag) {763console.error(" error: option `%s' argument missing, got `%s'", option.flags, flag);764} else {765console.error(" error: option `%s' argument missing", option.flags);766}767console.error();768process.exit(1);769};770771/**772* Unknown option `flag`.773*774* @param {String} flag775* @api private776*/777778Command.prototype.unknownOption = function(flag) {779if (this._allowUnknownOption) return;780console.error();781console.error(" error: unknown option `%s'", flag);782console.error();783process.exit(1);784};785786/**787* Variadic argument with `name` is not the last argument as required.788*789* @param {String} name790* @api private791*/792793Command.prototype.variadicArgNotLast = function(name) {794console.error();795console.error(" error: variadic arguments must be last `%s'", name);796console.error();797process.exit(1);798};799800/**801* Set the program version to `str`.802*803* This method auto-registers the "-V, --version" flag804* which will print the version number when passed.805*806* @param {String} str807* @param {String} flags808* @return {Command} for chaining809* @api public810*/811812Command.prototype.version = function(str, flags) {813if (0 == arguments.length) return this._version;814this._version = str;815flags = flags || '-V, --version';816this.option(flags, 'output the version number');817this.on('version', function() {818process.stdout.write(str + '\n');819process.exit(0);820});821return this;822};823824/**825* Set the description to `str`.826*827* @param {String} str828* @return {String|Command}829* @api public830*/831832Command.prototype.description = function(str) {833if (0 == arguments.length) return this._description;834this._description = str;835return this;836};837838/**839* Set an alias for the command840*841* @param {String} alias842* @return {String|Command}843* @api public844*/845846Command.prototype.alias = function(alias) {847if (0 == arguments.length) return this._alias;848this._alias = alias;849return this;850};851852/**853* Set / get the command usage `str`.854*855* @param {String} str856* @return {String|Command}857* @api public858*/859860Command.prototype.usage = function(str) {861var args = this._args.map(function(arg) {862return humanReadableArgName(arg);863});864865var usage = '[options]'866+ (this.commands.length ? ' [command]' : '')867+ (this._args.length ? ' ' + args.join(' ') : '');868869if (0 == arguments.length) return this._usage || usage;870this._usage = str;871872return this;873};874875/**876* Get the name of the command877*878* @param {String} name879* @return {String|Command}880* @api public881*/882883Command.prototype.name = function() {884return this._name;885};886887/**888* Return the largest option length.889*890* @return {Number}891* @api private892*/893894Command.prototype.largestOptionLength = function() {895return this.options.reduce(function(max, option) {896return Math.max(max, option.flags.length);897}, 0);898};899900/**901* Return help for options.902*903* @return {String}904* @api private905*/906907Command.prototype.optionHelp = function() {908var width = this.largestOptionLength();909910// Prepend the help information911return [pad('-h, --help', width) + ' ' + 'output usage information']912.concat(this.options.map(function(option) {913return pad(option.flags, width) + ' ' + option.description;914}))915.join('\n');916};917918/**919* Return command help documentation.920*921* @return {String}922* @api private923*/924925Command.prototype.commandHelp = function() {926if (!this.commands.length) return '';927928var commands = this.commands.filter(function(cmd) {929return !cmd._noHelp;930}).map(function(cmd) {931var args = cmd._args.map(function(arg) {932return humanReadableArgName(arg);933}).join(' ');934935return [936cmd._name937+ (cmd._alias938? '|' + cmd._alias939: '')940+ (cmd.options.length941? ' [options]'942: '')943+ ' ' + args944, cmd.description()945];946});947948var width = commands.reduce(function(max, command) {949return Math.max(max, command[0].length);950}, 0);951952return [953''954, ' Commands:'955, ''956, commands.map(function(cmd) {957return pad(cmd[0], width) + ' ' + cmd[1];958}).join('\n').replace(/^/gm, ' ')959, ''960].join('\n');961};962963/**964* Return program help documentation.965*966* @return {String}967* @api private968*/969970Command.prototype.helpInformation = function() {971var desc = [];972if (this._description) {973desc = [974' ' + this._description975, ''976];977}978979var cmdName = this._name;980if (this._alias) {981cmdName = cmdName + '|' + this._alias;982}983var usage = [984''985,' Usage: ' + cmdName + ' ' + this.usage()986, ''987];988989var cmds = [];990var commandHelp = this.commandHelp();991if (commandHelp) cmds = [commandHelp];992993var options = [994' Options:'995, ''996, '' + this.optionHelp().replace(/^/gm, ' ')997, ''998, ''999];10001001return usage1002.concat(cmds)1003.concat(desc)1004.concat(options)1005.join('\n');1006};10071008/**1009* Output help information for this command1010*1011* @api public1012*/10131014Command.prototype.outputHelp = function() {1015process.stdout.write(this.helpInformation());1016this.emit('--help');1017};10181019/**1020* Output help information and exit.1021*1022* @api public1023*/10241025Command.prototype.help = function() {1026this.outputHelp();1027process.exit();1028};10291030/**1031* Camel-case the given `flag`1032*1033* @param {String} flag1034* @return {String}1035* @api private1036*/10371038function camelcase(flag) {1039return flag.split('-').reduce(function(str, word) {1040return str + word[0].toUpperCase() + word.slice(1);1041});1042}10431044/**1045* Pad `str` to `width`.1046*1047* @param {String} str1048* @param {Number} width1049* @return {String}1050* @api private1051*/10521053function pad(str, width) {1054var len = Math.max(0, width - str.length);1055return str + Array(len + 1).join(' ');1056}10571058/**1059* Output help information if necessary1060*1061* @param {Command} command to output help for1062* @param {Array} array of options to search for -h or --help1063* @api private1064*/10651066function outputHelpIfNecessary(cmd, options) {1067options = options || [];1068for (var i = 0; i < options.length; i++) {1069if (options[i] == '--help' || options[i] == '-h') {1070cmd.outputHelp();1071process.exit(0);1072}1073}1074}10751076/**1077* Takes an argument an returns its human readable equivalent for help usage.1078*1079* @param {Object} arg1080* @return {String}1081* @api private1082*/10831084function humanReadableArgName(arg) {1085var nameOutput = arg.name + (arg.variadic === true ? '...' : '');10861087return arg.required1088? '<' + nameOutput + '>'1089: '[' + nameOutput + ']'1090}10911092// for versions before node v0.8 when there weren't `fs.existsSync`1093function exists(file) {1094try {1095if (fs.statSync(file).isFile()) {1096return true;1097}1098} catch (e) {1099return false;1100}1101}1102110311041105