react / react-0.13.3 / examples / basic-commonjs / node_modules / reactify / node_modules / react-tools / node_modules / commoner / node_modules / commander / index.js
81164 views1/**2* Module dependencies.3*/45var EventEmitter = require('events').EventEmitter;6var spawn = require('child_process').spawn;7var path = require('path');8var dirname = path.dirname;9var basename = path.basename;1011/**12* Expose the root command.13*/1415exports = module.exports = new Command();1617/**18* Expose `Command`.19*/2021exports.Command = Command;2223/**24* Expose `Option`.25*/2627exports.Option = Option;2829/**30* Initialize a new `Option` with the given `flags` and `description`.31*32* @param {String} flags33* @param {String} description34* @api public35*/3637function Option(flags, description) {38this.flags = flags;39this.required = ~flags.indexOf('<');40this.optional = ~flags.indexOf('[');41this.bool = !~flags.indexOf('-no-');42flags = flags.split(/[ ,|]+/);43if (flags.length > 1 && !/^[[<]/.test(flags[1])) this.short = flags.shift();44this.long = flags.shift();45this.description = description || '';46}4748/**49* Return option name.50*51* @return {String}52* @api private53*/5455Option.prototype.name = function() {56return this.long57.replace('--', '')58.replace('no-', '');59};6061/**62* Check if `arg` matches the short or long flag.63*64* @param {String} arg65* @return {Boolean}66* @api private67*/6869Option.prototype.is = function(arg) {70return arg == this.short || arg == this.long;71};7273/**74* Initialize a new `Command`.75*76* @param {String} name77* @api public78*/7980function Command(name) {81this.commands = [];82this.options = [];83this._execs = [];84this._args = [];85this._name = name;86}8788/**89* Inherit from `EventEmitter.prototype`.90*/9192Command.prototype.__proto__ = EventEmitter.prototype;9394/**95* Add command `name`.96*97* The `.action()` callback is invoked when the98* command `name` is specified via __ARGV__,99* and the remaining arguments are applied to the100* function for access.101*102* When the `name` is "*" an un-matched command103* will be passed as the first arg, followed by104* the rest of __ARGV__ remaining.105*106* Examples:107*108* program109* .version('0.0.1')110* .option('-C, --chdir <path>', 'change the working directory')111* .option('-c, --config <path>', 'set config path. defaults to ./deploy.conf')112* .option('-T, --no-tests', 'ignore test hook')113*114* program115* .command('setup')116* .description('run remote setup commands')117* .action(function() {118* console.log('setup');119* });120*121* program122* .command('exec <cmd>')123* .description('run the given remote command')124* .action(function(cmd) {125* console.log('exec "%s"', cmd);126* });127*128* program129* .command('teardown <dir> [otherDirs...]')130* .description('run teardown commands')131* .action(function(dir, otherDirs) {132* console.log('dir "%s"', dir);133* if (otherDirs) {134* otherDirs.forEach(function (oDir) {135* console.log('dir "%s"', oDir);136* });137* }138* });139*140* program141* .command('*')142* .description('deploy the given env')143* .action(function(env) {144* console.log('deploying "%s"', env);145* });146*147* program.parse(process.argv);148*149* @param {String} name150* @param {String} [desc] for git-style sub-commands151* @return {Command} the new command152* @api public153*/154155Command.prototype.command = function(name, desc) {156var args = name.split(/ +/);157var cmd = new Command(args.shift());158159if (desc) {160cmd.description(desc);161this.executables = true;162this._execs[cmd._name] = true;163}164165this.commands.push(cmd);166cmd.parseExpectedArgs(args);167cmd.parent = this;168169if (desc) return this;170return cmd;171};172173/**174* Add an implicit `help [cmd]` subcommand175* which invokes `--help` for the given command.176*177* @api private178*/179180Command.prototype.addImplicitHelpCommand = function() {181this.command('help [cmd]', 'display help for [cmd]');182};183184/**185* Parse expected `args`.186*187* For example `["[type]"]` becomes `[{ required: false, name: 'type' }]`.188*189* @param {Array} args190* @return {Command} for chaining191* @api public192*/193194Command.prototype.parseExpectedArgs = function(args) {195if (!args.length) return;196var self = this;197args.forEach(function(arg) {198var argDetails = {199required: false,200name: '',201variadic: false202};203204switch (arg[0]) {205case '<':206argDetails.required = true;207argDetails.name = arg.slice(1, -1);208break;209case '[':210argDetails.name = arg.slice(1, -1);211break;212}213214if (argDetails.name.length > 3 && argDetails.name.slice(-3) === '...') {215argDetails.variadic = true;216argDetails.name = argDetails.name.slice(0, -3);217}218if (argDetails.name) {219self._args.push(argDetails);220}221});222return this;223};224225/**226* Register callback `fn` for the command.227*228* Examples:229*230* program231* .command('help')232* .description('display verbose help')233* .action(function() {234* // output help here235* });236*237* @param {Function} fn238* @return {Command} for chaining239* @api public240*/241242Command.prototype.action = function(fn) {243var self = this;244var listener = function(args, unknown) {245// Parse any so-far unknown options246args = args || [];247unknown = unknown || [];248249var parsed = self.parseOptions(unknown);250251// Output help if necessary252outputHelpIfNecessary(self, parsed.unknown);253254// If there are still any unknown options, then we simply255// die, unless someone asked for help, in which case we give it256// to them, and then we die.257if (parsed.unknown.length > 0) {258self.unknownOption(parsed.unknown[0]);259}260261// Leftover arguments need to be pushed back. Fixes issue #56262if (parsed.args.length) args = parsed.args.concat(args);263264self._args.forEach(function(arg, i) {265if (arg.required && null == args[i]) {266self.missingArgument(arg.name);267} else if (arg.variadic) {268if (i !== self._args.length - 1) {269self.variadicArgNotLast(arg.name);270}271272args[i] = args.splice(i);273}274});275276// Always append ourselves to the end of the arguments,277// to make sure we match the number of arguments the user278// expects279if (self._args.length) {280args[self._args.length] = self;281} else {282args.push(self);283}284285fn.apply(self, args);286};287this.parent.on(this._name, listener);288if (this._alias) this.parent.on(this._alias, listener);289return this;290};291292/**293* Define option with `flags`, `description` and optional294* coercion `fn`.295*296* The `flags` string should contain both the short and long flags,297* separated by comma, a pipe or space. The following are all valid298* all will output this way when `--help` is used.299*300* "-p, --pepper"301* "-p|--pepper"302* "-p --pepper"303*304* Examples:305*306* // simple boolean defaulting to false307* program.option('-p, --pepper', 'add pepper');308*309* --pepper310* program.pepper311* // => Boolean312*313* // simple boolean defaulting to true314* program.option('-C, --no-cheese', 'remove cheese');315*316* program.cheese317* // => true318*319* --no-cheese320* program.cheese321* // => false322*323* // required argument324* program.option('-C, --chdir <path>', 'change the working directory');325*326* --chdir /tmp327* program.chdir328* // => "/tmp"329*330* // optional argument331* program.option('-c, --cheese [type]', 'add cheese [marble]');332*333* @param {String} flags334* @param {String} description335* @param {Function|Mixed} fn or default336* @param {Mixed} defaultValue337* @return {Command} for chaining338* @api public339*/340341Command.prototype.option = function(flags, description, fn, defaultValue) {342var self = this343, option = new Option(flags, description)344, oname = option.name()345, name = camelcase(oname);346347// default as 3rd arg348if (typeof fn != 'function') {349defaultValue = fn;350fn = null;351}352353// preassign default value only for --no-*, [optional], or <required>354if (false == option.bool || option.optional || option.required) {355// when --no-* we make sure default is true356if (false == option.bool) defaultValue = true;357// preassign only if we have a default358if (undefined !== defaultValue) self[name] = defaultValue;359}360361// register the option362this.options.push(option);363364// when it's passed assign the value365// and conditionally invoke the callback366this.on(oname, function(val) {367// coercion368if (null !== val && fn) val = fn(val, undefined === self[name]369? defaultValue370: self[name]);371372// unassigned or bool373if ('boolean' == typeof self[name] || 'undefined' == typeof self[name]) {374// if no value, bool true, and we have a default, then use it!375if (null == val) {376self[name] = option.bool377? defaultValue || true378: false;379} else {380self[name] = val;381}382} else if (null !== val) {383// reassign384self[name] = val;385}386});387388return this;389};390391/**392* Parse `argv`, settings options and invoking commands when defined.393*394* @param {Array} argv395* @return {Command} for chaining396* @api public397*/398399Command.prototype.parse = function(argv) {400// implicit help401if (this.executables) this.addImplicitHelpCommand();402403// store raw args404this.rawArgs = argv;405406// guess name407this._name = this._name || basename(argv[1], '.js');408409// process argv410var parsed = this.parseOptions(this.normalize(argv.slice(2)));411var args = this.args = parsed.args;412413var result = this.parseArgs(this.args, parsed.unknown);414415// executable sub-commands416var name = result.args[0];417if (this._execs[name] && typeof this._execs[name] != "function") {418return this.executeSubCommand(argv, args, parsed.unknown);419}420421return result;422};423424/**425* Execute a sub-command executable.426*427* @param {Array} argv428* @param {Array} args429* @param {Array} unknown430* @api private431*/432433Command.prototype.executeSubCommand = function(argv, args, unknown) {434args = args.concat(unknown);435436if (!args.length) this.help();437if ('help' == args[0] && 1 == args.length) this.help();438439// <cmd> --help440if ('help' == args[0]) {441args[0] = args[1];442args[1] = '--help';443}444445// executable446var dir = dirname(argv[1]);447var bin = basename(argv[1], '.js') + '-' + args[0];448449// check for ./<bin> first450var local = path.join(dir, bin);451452// run it453args = args.slice(1);454args.unshift(local);455var proc = spawn('node', args, { stdio: 'inherit', customFds: [0, 1, 2] });456proc.on('error', function(err) {457if (err.code == "ENOENT") {458console.error('\n %s(1) does not exist, try --help\n', bin);459} else if (err.code == "EACCES") {460console.error('\n %s(1) not executable. try chmod or run with root\n', bin);461}462});463464this.runningCommand = proc;465};466467/**468* Normalize `args`, splitting joined short flags. For example469* the arg "-abc" is equivalent to "-a -b -c".470* This also normalizes equal sign and splits "--abc=def" into "--abc def".471*472* @param {Array} args473* @return {Array}474* @api private475*/476477Command.prototype.normalize = function(args) {478var ret = []479, arg480, lastOpt481, index;482483for (var i = 0, len = args.length; i < len; ++i) {484arg = args[i];485if (i > 0) {486lastOpt = this.optionFor(args[i-1]);487}488489if (arg === '--') {490// Honor option terminator491ret = ret.concat(args.slice(i));492break;493} else if (lastOpt && lastOpt.required) {494ret.push(arg);495} else if (arg.length > 1 && '-' == arg[0] && '-' != arg[1]) {496arg.slice(1).split('').forEach(function(c) {497ret.push('-' + c);498});499} else if (/^--/.test(arg) && ~(index = arg.indexOf('='))) {500ret.push(arg.slice(0, index), arg.slice(index + 1));501} else {502ret.push(arg);503}504}505506return ret;507};508509/**510* Parse command `args`.511*512* When listener(s) are available those513* callbacks are invoked, otherwise the "*"514* event is emitted and those actions are invoked.515*516* @param {Array} args517* @return {Command} for chaining518* @api private519*/520521Command.prototype.parseArgs = function(args, unknown) {522var name;523524if (args.length) {525name = args[0];526if (this.listeners(name).length) {527this.emit(args.shift(), args, unknown);528} else {529this.emit('*', args);530}531} else {532outputHelpIfNecessary(this, unknown);533534// If there were no args and we have unknown options,535// then they are extraneous and we need to error.536if (unknown.length > 0) {537this.unknownOption(unknown[0]);538}539}540541return this;542};543544/**545* Return an option matching `arg` if any.546*547* @param {String} arg548* @return {Option}549* @api private550*/551552Command.prototype.optionFor = function(arg) {553for (var i = 0, len = this.options.length; i < len; ++i) {554if (this.options[i].is(arg)) {555return this.options[i];556}557}558};559560/**561* Parse options from `argv` returning `argv`562* void of these options.563*564* @param {Array} argv565* @return {Array}566* @api public567*/568569Command.prototype.parseOptions = function(argv) {570var args = []571, len = argv.length572, literal573, option574, arg;575576var unknownOptions = [];577578// parse options579for (var i = 0; i < len; ++i) {580arg = argv[i];581582// literal args after --583if ('--' == arg) {584literal = true;585continue;586}587588if (literal) {589args.push(arg);590continue;591}592593// find matching Option594option = this.optionFor(arg);595596// option is defined597if (option) {598// requires arg599if (option.required) {600arg = argv[++i];601if (null == arg) return this.optionMissingArgument(option);602this.emit(option.name(), arg);603// optional arg604} else if (option.optional) {605arg = argv[i+1];606if (null == arg || ('-' == arg[0] && '-' != arg)) {607arg = null;608} else {609++i;610}611this.emit(option.name(), arg);612// bool613} else {614this.emit(option.name());615}616continue;617}618619// looks like an option620if (arg.length > 1 && '-' == arg[0]) {621unknownOptions.push(arg);622623// If the next argument looks like it might be624// an argument for this option, we pass it on.625// If it isn't, then it'll simply be ignored626if (argv[i+1] && '-' != argv[i+1][0]) {627unknownOptions.push(argv[++i]);628}629continue;630}631632// arg633args.push(arg);634}635636return { args: args, unknown: unknownOptions };637};638639/**640* Return an object containing options as key-value pairs641*642* @return {Object}643* @api public644*/645Command.prototype.opts = function() {646var result = {}647, len = this.options.length;648649for (var i = 0 ; i < len; i++) {650var key = this.options[i].name();651result[key] = key === 'version' ? this._version : this[key];652}653return result;654};655656/**657* Argument `name` is missing.658*659* @param {String} name660* @api private661*/662663Command.prototype.missingArgument = function(name) {664console.error();665console.error(" error: missing required argument `%s'", name);666console.error();667process.exit(1);668};669670/**671* `Option` is missing an argument, but received `flag` or nothing.672*673* @param {String} option674* @param {String} flag675* @api private676*/677678Command.prototype.optionMissingArgument = function(option, flag) {679console.error();680if (flag) {681console.error(" error: option `%s' argument missing, got `%s'", option.flags, flag);682} else {683console.error(" error: option `%s' argument missing", option.flags);684}685console.error();686process.exit(1);687};688689/**690* Unknown option `flag`.691*692* @param {String} flag693* @api private694*/695696Command.prototype.unknownOption = function(flag) {697console.error();698console.error(" error: unknown option `%s'", flag);699console.error();700process.exit(1);701};702703/**704* Variadic argument with `name` is not the last argument as required.705*706* @param {String} name707* @api private708*/709710Command.prototype.variadicArgNotLast = function(name) {711console.error();712console.error(" error: variadic arguments must be last `%s'", name);713console.error();714process.exit(1);715};716717/**718* Set the program version to `str`.719*720* This method auto-registers the "-V, --version" flag721* which will print the version number when passed.722*723* @param {String} str724* @param {String} flags725* @return {Command} for chaining726* @api public727*/728729Command.prototype.version = function(str, flags) {730if (0 == arguments.length) return this._version;731this._version = str;732flags = flags || '-V, --version';733this.option(flags, 'output the version number');734this.on('version', function() {735process.stdout.write(str + '\n');736process.exit(0);737});738return this;739};740741/**742* Set the description to `str`.743*744* @param {String} str745* @return {String|Command}746* @api public747*/748749Command.prototype.description = function(str) {750if (0 == arguments.length) return this._description;751this._description = str;752return this;753};754755/**756* Set an alias for the command757*758* @param {String} alias759* @return {String|Command}760* @api public761*/762763Command.prototype.alias = function(alias) {764if (0 == arguments.length) return this._alias;765this._alias = alias;766return this;767};768769/**770* Set / get the command usage `str`.771*772* @param {String} str773* @return {String|Command}774* @api public775*/776777Command.prototype.usage = function(str) {778var args = this._args.map(function(arg) {779return humanReadableArgName(arg);780});781782var usage = '[options]'783+ (this.commands.length ? ' [command]' : '')784+ (this._args.length ? ' ' + args.join(' ') : '');785786if (0 == arguments.length) return this._usage || usage;787this._usage = str;788789return this;790};791792/**793* Get the name of the command794*795* @param {String} name796* @return {String|Command}797* @api public798*/799800Command.prototype.name = function(name) {801return this._name;802};803804/**805* Return the largest option length.806*807* @return {Number}808* @api private809*/810811Command.prototype.largestOptionLength = function() {812return this.options.reduce(function(max, option) {813return Math.max(max, option.flags.length);814}, 0);815};816817/**818* Return help for options.819*820* @return {String}821* @api private822*/823824Command.prototype.optionHelp = function() {825var width = this.largestOptionLength();826827// Prepend the help information828return [pad('-h, --help', width) + ' ' + 'output usage information']829.concat(this.options.map(function(option) {830return pad(option.flags, width) + ' ' + option.description;831}))832.join('\n');833};834835/**836* Return command help documentation.837*838* @return {String}839* @api private840*/841842Command.prototype.commandHelp = function() {843if (!this.commands.length) return '';844845var commands = this.commands.map(function(cmd) {846var args = cmd._args.map(function(arg) {847return humanReadableArgName(arg);848}).join(' ');849850return [851cmd._name852+ (cmd._alias853? '|' + cmd._alias854: '')855+ (cmd.options.length856? ' [options]'857: '')858+ ' ' + args859, cmd.description()860];861});862863var width = commands.reduce(function(max, command) {864return Math.max(max, command[0].length);865}, 0);866867return [868''869, ' Commands:'870, ''871, commands.map(function(cmd) {872return pad(cmd[0], width) + ' ' + cmd[1];873}).join('\n').replace(/^/gm, ' ')874, ''875].join('\n');876};877878/**879* Return program help documentation.880*881* @return {String}882* @api private883*/884885Command.prototype.helpInformation = function() {886return [887''888, ' Usage: ' + this._name889+ (this._alias890? '|' + this._alias891: '')892+ ' ' + this.usage()893, '' + this.commandHelp()894, ' Options:'895, ''896, '' + this.optionHelp().replace(/^/gm, ' ')897, ''898, ''899].join('\n');900};901902/**903* Output help information for this command904*905* @api public906*/907908Command.prototype.outputHelp = function() {909process.stdout.write(this.helpInformation());910this.emit('--help');911};912913/**914* Output help information and exit.915*916* @api public917*/918919Command.prototype.help = function() {920this.outputHelp();921process.exit();922};923924/**925* Camel-case the given `flag`926*927* @param {String} flag928* @return {String}929* @api private930*/931932function camelcase(flag) {933return flag.split('-').reduce(function(str, word) {934return str + word[0].toUpperCase() + word.slice(1);935});936}937938/**939* Pad `str` to `width`.940*941* @param {String} str942* @param {Number} width943* @return {String}944* @api private945*/946947function pad(str, width) {948var len = Math.max(0, width - str.length);949return str + Array(len + 1).join(' ');950}951952/**953* Output help information if necessary954*955* @param {Command} command to output help for956* @param {Array} array of options to search for -h or --help957* @api private958*/959960function outputHelpIfNecessary(cmd, options) {961options = options || [];962for (var i = 0; i < options.length; i++) {963if (options[i] == '--help' || options[i] == '-h') {964cmd.outputHelp();965process.exit(0);966}967}968}969970/**971* Takes an argument an returns its human readable equivalent for help usage.972*973* @param {Object} arg974* @return {String}975* @api private976*/977978function humanReadableArgName(arg) {979var nameOutput = arg.name + (arg.variadic === true ? '...' : '');980981return arg.required982? '<' + nameOutput + '>'983: '[' + nameOutput + ']'984}985986987