UNPKG

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