UNPKG

25.6 kBJavaScriptView Raw
1/**
2 * Module dependencies.
3 */
4
5var EventEmitter = require('events').EventEmitter;
6var spawn = require('child_process').spawn;
7var path = require('path');
8var dirname = path.dirname;
9var basename = path.basename;
10var fs = require('fs');
11
12/**
13 * Expose the root command.
14 */
15
16exports = module.exports = new Command();
17
18/**
19 * Expose `Command`.
20 */
21
22exports.Command = Command;
23
24/**
25 * Expose `Option`.
26 */
27
28exports.Option = Option;
29
30/**
31 * Initialize a new `Option` with the given `flags` and `description`.
32 *
33 * @param {String} flags
34 * @param {String} description
35 * @api public
36 */
37
38function Option(flags, description) {
39 this.flags = flags;
40 this.required = ~flags.indexOf('<');
41 this.optional = ~flags.indexOf('[');
42 this.bool = !~flags.indexOf('-no-');
43 flags = flags.split(/[ ,|]+/);
44 if (flags.length > 1 && !/^[[<]/.test(flags[1])) this.short = flags.shift();
45 this.long = flags.shift();
46 this.description = description || '';
47}
48
49/**
50 * Return option name.
51 *
52 * @return {String}
53 * @api private
54 */
55
56Option.prototype.name = function() {
57 return this.long
58 .replace('--', '')
59 .replace('no-', '');
60};
61
62/**
63 * Check if `arg` matches the short or long flag.
64 *
65 * @param {String} arg
66 * @return {Boolean}
67 * @api private
68 */
69
70Option.prototype.is = function(arg) {
71 return arg == this.short || arg == this.long;
72};
73
74/**
75 * Initialize a new `Command`.
76 *
77 * @param {String} name
78 * @api public
79 */
80
81function Command(name) {
82 this.commands = [];
83 this.options = [];
84 this._execs = {};
85 this._allowUnknownOption = false;
86 this._args = [];
87 this._name = name || '';
88}
89
90/**
91 * Inherit from `EventEmitter.prototype`.
92 */
93
94Command.prototype.__proto__ = EventEmitter.prototype;
95
96/**
97 * Add command `name`.
98 *
99 * The `.action()` callback is invoked when the
100 * command `name` is specified via __ARGV__,
101 * and the remaining arguments are applied to the
102 * function for access.
103 *
104 * When the `name` is "*" an un-matched command
105 * will be passed as the first arg, followed by
106 * the rest of __ARGV__ remaining.
107 *
108 * Examples:
109 *
110 * program
111 * .version('0.0.1')
112 * .option('-C, --chdir <path>', 'change the working directory')
113 * .option('-c, --config <path>', 'set config path. defaults to ./deploy.conf')
114 * .option('-T, --no-tests', 'ignore test hook')
115 *
116 * program
117 * .command('setup')
118 * .description('run remote setup commands')
119 * .action(function() {
120 * console.log('setup');
121 * });
122 *
123 * program
124 * .command('exec <cmd>')
125 * .description('run the given remote command')
126 * .action(function(cmd) {
127 * console.log('exec "%s"', cmd);
128 * });
129 *
130 * program
131 * .command('teardown <dir> [otherDirs...]')
132 * .description('run teardown commands')
133 * .action(function(dir, otherDirs) {
134 * console.log('dir "%s"', dir);
135 * if (otherDirs) {
136 * otherDirs.forEach(function (oDir) {
137 * console.log('dir "%s"', oDir);
138 * });
139 * }
140 * });
141 *
142 * program
143 * .command('*')
144 * .description('deploy the given env')
145 * .action(function(env) {
146 * console.log('deploying "%s"', env);
147 * });
148 *
149 * program.parse(process.argv);
150 *
151 * @param {String} name
152 * @param {String} [desc] for git-style sub-commands
153 * @return {Command} the new command
154 * @api public
155 */
156
157Command.prototype.command = function(name, desc, opts) {
158 opts = opts || {};
159 var args = name.split(/ +/);
160 var cmd = new Command(args.shift());
161
162 if (desc) {
163 cmd.description(desc);
164 this.executables = true;
165 this._execs[cmd._name] = true;
166 if (opts.isDefault) this.defaultExecutable = cmd._name;
167 }
168
169 cmd._noHelp = !!opts.noHelp;
170 this.commands.push(cmd);
171 cmd.parseExpectedArgs(args);
172 cmd.parent = this;
173
174 if (desc) return this;
175 return cmd;
176};
177
178/**
179 * Define argument syntax for the top-level command.
180 *
181 * @api public
182 */
183
184Command.prototype.arguments = function (desc) {
185 return this.parseExpectedArgs(desc.split(/ +/));
186};
187
188/**
189 * Add an implicit `help [cmd]` subcommand
190 * which invokes `--help` for the given command.
191 *
192 * @api private
193 */
194
195Command.prototype.addImplicitHelpCommand = function() {
196 this.command('help [cmd]', 'display help for [cmd]');
197};
198
199/**
200 * Parse expected `args`.
201 *
202 * For example `["[type]"]` becomes `[{ required: false, name: 'type' }]`.
203 *
204 * @param {Array} args
205 * @return {Command} for chaining
206 * @api public
207 */
208
209Command.prototype.parseExpectedArgs = function(args) {
210 if (!args.length) return;
211 var self = this;
212 args.forEach(function(arg) {
213 var argDetails = {
214 required: false,
215 name: '',
216 variadic: false
217 };
218
219 switch (arg[0]) {
220 case '<':
221 argDetails.required = true;
222 argDetails.name = arg.slice(1, -1);
223 break;
224 case '[':
225 argDetails.name = arg.slice(1, -1);
226 break;
227 }
228
229 if (argDetails.name.length > 3 && argDetails.name.slice(-3) === '...') {
230 argDetails.variadic = true;
231 argDetails.name = argDetails.name.slice(0, -3);
232 }
233 if (argDetails.name) {
234 self._args.push(argDetails);
235 }
236 });
237 return this;
238};
239
240/**
241 * Register callback `fn` for the command.
242 *
243 * Examples:
244 *
245 * program
246 * .command('help')
247 * .description('display verbose help')
248 * .action(function() {
249 * // output help here
250 * });
251 *
252 * @param {Function} fn
253 * @return {Command} for chaining
254 * @api public
255 */
256
257Command.prototype.action = function(fn) {
258 var self = this;
259 var listener = function(args, unknown) {
260 // Parse any so-far unknown options
261 args = args || [];
262 unknown = unknown || [];
263
264 var parsed = self.parseOptions(unknown);
265
266 // Output help if necessary
267 outputHelpIfNecessary(self, parsed.unknown);
268
269 // If there are still any unknown options, then we simply
270 // die, unless someone asked for help, in which case we give it
271 // to them, and then we die.
272 if (parsed.unknown.length > 0) {
273 self.unknownOption(parsed.unknown[0]);
274 }
275
276 // Leftover arguments need to be pushed back. Fixes issue #56
277 if (parsed.args.length) args = parsed.args.concat(args);
278
279 self._args.forEach(function(arg, i) {
280 if (arg.required && null == args[i]) {
281 self.missingArgument(arg.name);
282 } else if (arg.variadic) {
283 if (i !== self._args.length - 1) {
284 self.variadicArgNotLast(arg.name);
285 }
286
287 args[i] = args.splice(i);
288 }
289 });
290
291 // Always append ourselves to the end of the arguments,
292 // to make sure we match the number of arguments the user
293 // expects
294 if (self._args.length) {
295 args[self._args.length] = self;
296 } else {
297 args.push(self);
298 }
299
300 fn.apply(self, args);
301 };
302 var parent = this.parent || this;
303 var name = parent === this ? '*' : this._name;
304 parent.on('command:' + name, listener);
305 if (this._alias) parent.on('command:' + this._alias, listener);
306 return this;
307};
308
309/**
310 * Define option with `flags`, `description` and optional
311 * 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 valid
315 * 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 false
324 * program.option('-p, --pepper', 'add pepper');
325 *
326 * --pepper
327 * program.pepper
328 * // => Boolean
329 *
330 * // simple boolean defaulting to true
331 * program.option('-C, --no-cheese', 'remove cheese');
332 *
333 * program.cheese
334 * // => true
335 *
336 * --no-cheese
337 * program.cheese
338 * // => false
339 *
340 * // required argument
341 * program.option('-C, --chdir <path>', 'change the working directory');
342 *
343 * --chdir /tmp
344 * program.chdir
345 * // => "/tmp"
346 *
347 * // optional argument
348 * program.option('-c, --cheese [type]', 'add cheese [marble]');
349 *
350 * @param {String} flags
351 * @param {String} description
352 * @param {Function|*} [fn] or default
353 * @param {*} [defaultValue]
354 * @return {Command} for chaining
355 * @api public
356 */
357
358Command.prototype.option = function(flags, description, fn, defaultValue) {
359 var self = this
360 , option = new Option(flags, description)
361 , oname = option.name()
362 , name = camelcase(oname);
363
364 // default as 3rd arg
365 if (typeof fn != 'function') {
366 if (fn instanceof RegExp) {
367 var regex = fn;
368 fn = function(val, def) {
369 var m = regex.exec(val);
370 return m ? m[0] : def;
371 }
372 }
373 else {
374 defaultValue = fn;
375 fn = null;
376 }
377 }
378
379 // preassign default value only for --no-*, [optional], or <required>
380 if (false == option.bool || option.optional || option.required) {
381 // when --no-* we make sure default is true
382 if (false == option.bool) defaultValue = true;
383 // preassign only if we have a default
384 if (undefined !== defaultValue) self[name] = defaultValue;
385 }
386
387 // register the option
388 this.options.push(option);
389
390 // when it's passed assign the value
391 // and conditionally invoke the callback
392 this.on('option:' + oname, function(val) {
393 // coercion
394 if (null !== val && fn) val = fn(val, undefined === self[name]
395 ? defaultValue
396 : self[name]);
397
398 // unassigned or bool
399 if ('boolean' == typeof self[name] || 'undefined' == typeof self[name]) {
400 // if no value, bool true, and we have a default, then use it!
401 if (null == val) {
402 self[name] = option.bool
403 ? defaultValue || true
404 : false;
405 } else {
406 self[name] = val;
407 }
408 } else if (null !== val) {
409 // reassign
410 self[name] = val;
411 }
412 });
413
414 return this;
415};
416
417/**
418 * Allow unknown options on the command line.
419 *
420 * @param {Boolean} arg if `true` or omitted, no error will be thrown
421 * for unknown options.
422 * @api public
423 */
424Command.prototype.allowUnknownOption = function(arg) {
425 this._allowUnknownOption = arguments.length === 0 || arg;
426 return this;
427};
428
429/**
430 * Parse `argv`, settings options and invoking commands when defined.
431 *
432 * @param {Array} argv
433 * @return {Command} for chaining
434 * @api public
435 */
436
437Command.prototype.parse = function(argv) {
438 // implicit help
439 if (this.executables) this.addImplicitHelpCommand();
440
441 // store raw args
442 this.rawArgs = argv;
443
444 // guess name
445 this._name = this._name || basename(argv[1], '.js');
446
447 // github-style sub-commands with no sub-command
448 if (this.executables && argv.length < 3 && !this.defaultExecutable) {
449 // this user needs help
450 argv.push('--help');
451 }
452
453 // process argv
454 var parsed = this.parseOptions(this.normalize(argv.slice(2)));
455 var args = this.args = parsed.args;
456
457 var result = this.parseArgs(this.args, parsed.unknown);
458
459 // executable sub-commands
460 var name = result.args[0];
461
462 var aliasCommand = null;
463 // check alias of sub commands
464 if (name) {
465 aliasCommand = this.commands.filter(function(command) {
466 return command.alias() === name;
467 })[0];
468 }
469
470 if (this._execs[name] && typeof this._execs[name] != "function") {
471 return this.executeSubCommand(argv, args, parsed.unknown);
472 } else if (aliasCommand) {
473 // is alias of a subCommand
474 args[0] = aliasCommand._name;
475 return this.executeSubCommand(argv, args, parsed.unknown);
476 } else if (this.defaultExecutable) {
477 // use the default subcommand
478 args.unshift(this.defaultExecutable);
479 return this.executeSubCommand(argv, args, parsed.unknown);
480 }
481
482 return result;
483};
484
485/**
486 * Execute a sub-command executable.
487 *
488 * @param {Array} argv
489 * @param {Array} args
490 * @param {Array} unknown
491 * @api private
492 */
493
494Command.prototype.executeSubCommand = function(argv, args, unknown) {
495 args = args.concat(unknown);
496
497 if (!args.length) this.help();
498 if ('help' == args[0] && 1 == args.length) this.help();
499
500 // <cmd> --help
501 if ('help' == args[0]) {
502 args[0] = args[1];
503 args[1] = '--help';
504 }
505
506 // executable
507 var f = argv[1];
508 // name of the subcommand, link `pm-install`
509 var bin = basename(f, '.js') + '-' + args[0];
510
511
512 // In case of globally installed, get the base dir where executable
513 // subcommand file should be located at
514 var baseDir
515 , link = fs.lstatSync(f).isSymbolicLink() ? fs.readlinkSync(f) : f;
516
517 // when symbolink is relative path
518 if (link !== f && link.charAt(0) !== '/') {
519 link = path.join(dirname(f), link)
520 }
521 baseDir = dirname(link);
522
523 // prefer local `./<bin>` to bin in the $PATH
524 var localBin = path.join(baseDir, bin);
525
526 // whether bin file is a js script with explicit `.js` extension
527 var isExplicitJS = false;
528 if (exists(localBin + '.js')) {
529 bin = localBin + '.js';
530 isExplicitJS = true;
531 } else if (exists(localBin)) {
532 bin = localBin;
533 }
534
535 args = args.slice(1);
536
537 var proc;
538 if (process.platform !== 'win32') {
539 if (isExplicitJS) {
540 args.unshift(bin);
541 // add executable arguments to spawn
542 args = (process.execArgv || []).concat(args);
543
544 proc = spawn('node', args, { stdio: 'inherit', customFds: [0, 1, 2] });
545 } else {
546 proc = spawn(bin, args, { stdio: 'inherit', customFds: [0, 1, 2] });
547 }
548 } else {
549 args.unshift(bin);
550 proc = spawn(process.execPath, args, { stdio: 'inherit'});
551 }
552
553 var signals = ['SIGUSR1', 'SIGUSR2', 'SIGTERM', 'SIGINT', 'SIGHUP'];
554 signals.forEach(function(signal) {
555 process.on(signal, function(){
556 if ((proc.killed === false) && (proc.exitCode === null)){
557 proc.kill(signal);
558 }
559 });
560 });
561 proc.on('close', process.exit.bind(process));
562 proc.on('error', function(err) {
563 if (err.code == "ENOENT") {
564 console.error('\n %s(1) does not exist, try --help\n', bin);
565 } else if (err.code == "EACCES") {
566 console.error('\n %s(1) not executable. try chmod or run with root\n', bin);
567 }
568 process.exit(1);
569 });
570
571 // Store the reference to the child process
572 this.runningCommand = proc;
573};
574
575/**
576 * Normalize `args`, splitting joined short flags. For example
577 * the arg "-abc" is equivalent to "-a -b -c".
578 * This also normalizes equal sign and splits "--abc=def" into "--abc def".
579 *
580 * @param {Array} args
581 * @return {Array}
582 * @api private
583 */
584
585Command.prototype.normalize = function(args) {
586 var ret = []
587 , arg
588 , lastOpt
589 , index;
590
591 for (var i = 0, len = args.length; i < len; ++i) {
592 arg = args[i];
593 if (i > 0) {
594 lastOpt = this.optionFor(args[i-1]);
595 }
596
597 if (arg === '--') {
598 // Honor option terminator
599 ret = ret.concat(args.slice(i));
600 break;
601 } else if (lastOpt && lastOpt.required) {
602 ret.push(arg);
603 } else if (arg.length > 1 && '-' == arg[0] && '-' != arg[1]) {
604 arg.slice(1).split('').forEach(function(c) {
605 ret.push('-' + c);
606 });
607 } else if (/^--/.test(arg) && ~(index = arg.indexOf('='))) {
608 ret.push(arg.slice(0, index), arg.slice(index + 1));
609 } else {
610 ret.push(arg);
611 }
612 }
613
614 return ret;
615};
616
617/**
618 * Parse command `args`.
619 *
620 * When listener(s) are available those
621 * callbacks are invoked, otherwise the "*"
622 * event is emitted and those actions are invoked.
623 *
624 * @param {Array} args
625 * @return {Command} for chaining
626 * @api private
627 */
628
629Command.prototype.parseArgs = function(args, unknown) {
630 var name;
631
632 if (args.length) {
633 name = args[0];
634 if (this.listeners('command:' + name).length) {
635 this.emit('command:' + args.shift(), args, unknown);
636 } else {
637 this.emit('command:*', args);
638 }
639 } else {
640 outputHelpIfNecessary(this, unknown);
641
642 // If there were no args and we have unknown options,
643 // then they are extraneous and we need to error.
644 if (unknown.length > 0) {
645 this.unknownOption(unknown[0]);
646 }
647 }
648
649 return this;
650};
651
652/**
653 * Return an option matching `arg` if any.
654 *
655 * @param {String} arg
656 * @return {Option}
657 * @api private
658 */
659
660Command.prototype.optionFor = function(arg) {
661 for (var i = 0, len = this.options.length; i < len; ++i) {
662 if (this.options[i].is(arg)) {
663 return this.options[i];
664 }
665 }
666};
667
668/**
669 * Parse options from `argv` returning `argv`
670 * void of these options.
671 *
672 * @param {Array} argv
673 * @return {Array}
674 * @api public
675 */
676
677Command.prototype.parseOptions = function(argv) {
678 var args = []
679 , len = argv.length
680 , literal
681 , option
682 , arg;
683
684 var unknownOptions = [];
685
686 // parse options
687 for (var i = 0; i < len; ++i) {
688 arg = argv[i];
689
690 // literal args after --
691 if (literal) {
692 args.push(arg);
693 continue;
694 }
695
696 if ('--' == arg) {
697 literal = true;
698 continue;
699 }
700
701 // find matching Option
702 option = this.optionFor(arg);
703
704 // option is defined
705 if (option) {
706 // requires arg
707 if (option.required) {
708 arg = argv[++i];
709 if (null == arg) return this.optionMissingArgument(option);
710 this.emit('option:' + option.name(), arg);
711 // optional arg
712 } else if (option.optional) {
713 arg = argv[i+1];
714 if (null == arg || ('-' == arg[0] && '-' != arg)) {
715 arg = null;
716 } else {
717 ++i;
718 }
719 this.emit('option:' + option.name(), arg);
720 // bool
721 } else {
722 this.emit('option:' + option.name());
723 }
724 continue;
725 }
726
727 // looks like an option
728 if (arg.length > 1 && '-' == arg[0]) {
729 unknownOptions.push(arg);
730
731 // If the next argument looks like it might be
732 // an argument for this option, we pass it on.
733 // If it isn't, then it'll simply be ignored
734 if (argv[i+1] && '-' != argv[i+1][0]) {
735 unknownOptions.push(argv[++i]);
736 }
737 continue;
738 }
739
740 // arg
741 args.push(arg);
742 }
743
744 return { args: args, unknown: unknownOptions };
745};
746
747/**
748 * Return an object containing options as key-value pairs
749 *
750 * @return {Object}
751 * @api public
752 */
753Command.prototype.opts = function() {
754 var result = {}
755 , len = this.options.length;
756
757 for (var i = 0 ; i < len; i++) {
758 var key = camelcase(this.options[i].name());
759 result[key] = key === 'version' ? this._version : this[key];
760 }
761 return result;
762};
763
764/**
765 * Argument `name` is missing.
766 *
767 * @param {String} name
768 * @api private
769 */
770
771Command.prototype.missingArgument = function(name) {
772 console.error();
773 console.error(" error: missing required argument `%s'", name);
774 console.error();
775 process.exit(1);
776};
777
778/**
779 * `Option` is missing an argument, but received `flag` or nothing.
780 *
781 * @param {String} option
782 * @param {String} flag
783 * @api private
784 */
785
786Command.prototype.optionMissingArgument = function(option, flag) {
787 console.error();
788 if (flag) {
789 console.error(" error: option `%s' argument missing, got `%s'", option.flags, flag);
790 } else {
791 console.error(" error: option `%s' argument missing", option.flags);
792 }
793 console.error();
794 process.exit(1);
795};
796
797/**
798 * Unknown option `flag`.
799 *
800 * @param {String} flag
801 * @api private
802 */
803
804Command.prototype.unknownOption = function(flag) {
805 if (this._allowUnknownOption) return;
806 console.error();
807 console.error(" error: unknown option `%s'", flag);
808 console.error();
809 process.exit(1);
810};
811
812/**
813 * Variadic argument with `name` is not the last argument as required.
814 *
815 * @param {String} name
816 * @api private
817 */
818
819Command.prototype.variadicArgNotLast = function(name) {
820 console.error();
821 console.error(" error: variadic arguments must be last `%s'", name);
822 console.error();
823 process.exit(1);
824};
825
826/**
827 * Set the program version to `str`.
828 *
829 * This method auto-registers the "-V, --version" flag
830 * which will print the version number when passed.
831 *
832 * @param {String} str
833 * @param {String} [flags]
834 * @return {Command} for chaining
835 * @api public
836 */
837
838Command.prototype.version = function(str, flags) {
839 if (0 == arguments.length) return this._version;
840 this._version = str;
841 flags = flags || '-V, --version';
842 this.option(flags, 'output the version number');
843 this.on('option:version', function() {
844 process.stdout.write(str + '\n');
845 process.exit(0);
846 });
847 return this;
848};
849
850/**
851 * Set the description to `str`.
852 *
853 * @param {String} str
854 * @return {String|Command}
855 * @api public
856 */
857
858Command.prototype.description = function(str) {
859 if (0 === arguments.length) return this._description;
860 this._description = str;
861 return this;
862};
863
864/**
865 * Set an alias for the command
866 *
867 * @param {String} alias
868 * @return {String|Command}
869 * @api public
870 */
871
872Command.prototype.alias = function(alias) {
873 var command = this;
874 if(this.commands.length !== 0) {
875 command = this.commands[this.commands.length - 1]
876 }
877
878 if (arguments.length === 0) return command._alias;
879
880 command._alias = alias;
881 return this;
882};
883
884/**
885 * Set / get the command usage `str`.
886 *
887 * @param {String} str
888 * @return {String|Command}
889 * @api public
890 */
891
892Command.prototype.usage = function(str) {
893 var args = this._args.map(function(arg) {
894 return humanReadableArgName(arg);
895 });
896
897 var usage = '[options]'
898 + (this.commands.length ? ' [command]' : '')
899 + (this._args.length ? ' ' + args.join(' ') : '');
900
901 if (0 == arguments.length) return this._usage || usage;
902 this._usage = str;
903
904 return this;
905};
906
907/**
908 * Get or set the name of the command
909 *
910 * @param {String} str
911 * @return {String|Command}
912 * @api public
913 */
914
915Command.prototype.name = function(str) {
916 if (0 === arguments.length) return this._name;
917 this._name = str;
918 return this;
919};
920
921/**
922 * Return the largest option length.
923 *
924 * @return {Number}
925 * @api private
926 */
927
928Command.prototype.largestOptionLength = function() {
929 return this.options.reduce(function(max, option) {
930 return Math.max(max, option.flags.length);
931 }, 0);
932};
933
934/**
935 * Return help for options.
936 *
937 * @return {String}
938 * @api private
939 */
940
941Command.prototype.optionHelp = function() {
942 var width = this.largestOptionLength();
943
944 // Append the help information
945 return this.options.map(function(option) {
946 return pad(option.flags, width) + ' ' + option.description;
947 }).concat([pad('-h, --help', width) + ' ' + 'output usage information'])
948 .join('\n');
949};
950
951/**
952 * Return command help documentation.
953 *
954 * @return {String}
955 * @api private
956 */
957
958Command.prototype.commandHelp = function() {
959 if (!this.commands.length) return '';
960
961 var commands = this.commands.filter(function(cmd) {
962 return !cmd._noHelp;
963 }).map(function(cmd) {
964 var args = cmd._args.map(function(arg) {
965 return humanReadableArgName(arg);
966 }).join(' ');
967
968 return [
969 cmd._name
970 + (cmd._alias ? '|' + cmd._alias : '')
971 + (cmd.options.length ? ' [options]' : '')
972 + ' ' + args
973 , cmd._description
974 ];
975 });
976
977 var width = commands.reduce(function(max, command) {
978 return Math.max(max, command[0].length);
979 }, 0);
980
981 return [
982 ''
983 , ' Commands:'
984 , ''
985 , commands.map(function(cmd) {
986 var desc = cmd[1] ? ' ' + cmd[1] : '';
987 return pad(cmd[0], width) + desc;
988 }).join('\n').replace(/^/gm, ' ')
989 , ''
990 ].join('\n');
991};
992
993/**
994 * Return program help documentation.
995 *
996 * @return {String}
997 * @api private
998 */
999
1000Command.prototype.helpInformation = function() {
1001 var desc = [];
1002 if (this._description) {
1003 desc = [
1004 ' ' + this._description
1005 , ''
1006 ];
1007 }
1008
1009 var cmdName = this._name;
1010 if (this._alias) {
1011 cmdName = cmdName + '|' + this._alias;
1012 }
1013 var usage = [
1014 ''
1015 ,' Usage: ' + cmdName + ' ' + this.usage()
1016 , ''
1017 ];
1018
1019 var cmds = [];
1020 var commandHelp = this.commandHelp();
1021 if (commandHelp) cmds = [commandHelp];
1022
1023 var options = [
1024 ''
1025 , ' Options:'
1026 , ''
1027 , '' + this.optionHelp().replace(/^/gm, ' ')
1028 , ''
1029 ];
1030
1031 return usage
1032 .concat(desc)
1033 .concat(options)
1034 .concat(cmds)
1035 .join('\n');
1036};
1037
1038/**
1039 * Output help information for this command
1040 *
1041 * @api public
1042 */
1043
1044Command.prototype.outputHelp = function(cb) {
1045 if (!cb) {
1046 cb = function(passthru) {
1047 return passthru;
1048 }
1049 }
1050 process.stdout.write(cb(this.helpInformation()));
1051 this.emit('--help');
1052};
1053
1054/**
1055 * Output help information and exit.
1056 *
1057 * @api public
1058 */
1059
1060Command.prototype.help = function(cb) {
1061 this.outputHelp(cb);
1062 process.exit();
1063};
1064
1065/**
1066 * Camel-case the given `flag`
1067 *
1068 * @param {String} flag
1069 * @return {String}
1070 * @api private
1071 */
1072
1073function camelcase(flag) {
1074 return flag.split('-').reduce(function(str, word) {
1075 return str + word[0].toUpperCase() + word.slice(1);
1076 });
1077}
1078
1079/**
1080 * Pad `str` to `width`.
1081 *
1082 * @param {String} str
1083 * @param {Number} width
1084 * @return {String}
1085 * @api private
1086 */
1087
1088function pad(str, width) {
1089 var len = Math.max(0, width - str.length);
1090 return str + Array(len + 1).join(' ');
1091}
1092
1093/**
1094 * Output help information if necessary
1095 *
1096 * @param {Command} command to output help for
1097 * @param {Array} array of options to search for -h or --help
1098 * @api private
1099 */
1100
1101function outputHelpIfNecessary(cmd, options) {
1102 options = options || [];
1103 for (var i = 0; i < options.length; i++) {
1104 if (options[i] == '--help' || options[i] == '-h') {
1105 cmd.outputHelp();
1106 process.exit(0);
1107 }
1108 }
1109}
1110
1111/**
1112 * Takes an argument an returns its human readable equivalent for help usage.
1113 *
1114 * @param {Object} arg
1115 * @return {String}
1116 * @api private
1117 */
1118
1119function humanReadableArgName(arg) {
1120 var nameOutput = arg.name + (arg.variadic === true ? '...' : '');
1121
1122 return arg.required
1123 ? '<' + nameOutput + '>'
1124 : '[' + nameOutput + ']'
1125}
1126
1127// for versions before node v0.8 when there weren't `fs.existsSync`
1128function exists(file) {
1129 try {
1130 if (fs.statSync(file).isFile()) {
1131 return true;
1132 }
1133 } catch (e) {
1134 return false;
1135 }
1136}
1137