comunicationLeap/node_modules/commander/lib/commander.js
node_modules/commander/lib/commander.jsRaw
1
2/*!
3 * commander
4 * Copyright(c) 2011 TJ Holowaychuk <tj@vision-media.ca>
5 * MIT Licensed
6 */
7
8/**
9 * Module dependencies.
10 */
11
12var EventEmitter = require('events').EventEmitter
13 , path = require('path')
14 , tty = require('tty')
15 , basename = path.basename;
16
17/**
18 * Expose the root command.
19 */
20
21exports = module.exports = new Command;
22
23/**
24 * Expose `Command`.
25 */
26
27exports.Command = Command;
28
29/**
30 * Expose `Option`.
31 */
32
33exports.Option = Option;
34
35/**
36 * Initialize a new `Option` with the given `flags` and `description`.
37 *
38 * @param {String} flags
39 * @param {String} description
40 * @api public
41 */
42
43function Option(flags, description) {
44 this.flags = flags;
45 this.required = ~flags.indexOf('<');
46 this.optional = ~flags.indexOf('[');
47 this.bool = !~flags.indexOf('-no-');
48 flags = flags.split(/[ ,|]+/);
49 if (flags.length > 1 && !/^[[<]/.test(flags[1])) this.short = flags.shift();
50 this.long = flags.shift();
51 this.description = description;
52}
53
54/**
55 * Return option name.
56 *
57 * @return {String}
58 * @api private
59 */
60
61Option.prototype.name = function(){
62 return this.long
63 .replace('--', '')
64 .replace('no-', '');
65};
66
67/**
68 * Check if `arg` matches the short or long flag.
69 *
70 * @param {String} arg
71 * @return {Boolean}
72 * @api private
73 */
74
75Option.prototype.is = function(arg){
76 return arg == this.short
77 || arg == this.long;
78};
79
80/**
81 * Initialize a new `Command`.
82 *
83 * @param {String} name
84 * @api public
85 */
86
87function Command(name) {
88 this.commands = [];
89 this.options = [];
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('*')
136 * .description('deploy the given env')
137 * .action(function(env){
138 * console.log('deploying "%s"', env);
139 * });
140 *
141 * program.parse(process.argv);
142 *
143 * @param {String} name
144 * @return {Command} the new command
145 * @api public
146 */
147
148Command.prototype.command = function(name){
149 var args = name.split(/ +/);
150 var cmd = new Command(args.shift());
151 this.commands.push(cmd);
152 cmd.parseExpectedArgs(args);
153 cmd.parent = this;
154 return cmd;
155};
156
157/**
158 * Parse expected `args`.
159 *
160 * For example `["[type]"]` becomes `[{ required: false, name: 'type' }]`.
161 *
162 * @param {Array} args
163 * @return {Command} for chaining
164 * @api public
165 */
166
167Command.prototype.parseExpectedArgs = function(args){
168 if (!args.length) return;
169 var self = this;
170 args.forEach(function(arg){
171 switch (arg[0]) {
172 case '<':
173 self.args.push({ required: true, name: arg.slice(1, -1) });
174 break;
175 case '[':
176 self.args.push({ required: false, name: arg.slice(1, -1) });
177 break;
178 }
179 });
180 return this;
181};
182
183/**
184 * Register callback `fn` for the command.
185 *
186 * Examples:
187 *
188 * program
189 * .command('help')
190 * .description('display verbose help')
191 * .action(function(){
192 * // output help here
193 * });
194 *
195 * @param {Function} fn
196 * @return {Command} for chaining
197 * @api public
198 */
199
200Command.prototype.action = function(fn){
201 var self = this;
202 this.parent.on(this.name, function(args, unknown){
203 // Parse any so-far unknown options
204 unknown = unknown || [];
205 var parsed = self.parseOptions(unknown);
206
207 // Output help if necessary
208 outputHelpIfNecessary(self, parsed.unknown);
209
210 // If there are still any unknown options, then we simply
211 // die, unless someone asked for help, in which case we give it
212 // to them, and then we die.
213 if (parsed.unknown.length > 0) {
214 self.unknownOption(parsed.unknown[0]);
215 }
216
217 self.args.forEach(function(arg, i){
218 if (arg.required && null == args[i]) {
219 self.missingArgument(arg.name);
220 }
221 });
222
223 // Always append ourselves to the end of the arguments,
224 // to make sure we match the number of arguments the user
225 // expects
226 if (self.args.length) {
227 args[self.args.length] = self;
228 } else {
229 args.push(self);
230 }
231
232 fn.apply(this, args);
233 });
234 return this;
235};
236
237/**
238 * Define option with `flags`, `description` and optional
239 * coercion `fn`.
240 *
241 * The `flags` string should contain both the short and long flags,
242 * separated by comma, a pipe or space. The following are all valid
243 * all will output this way when `--help` is used.
244 *
245 * "-p, --pepper"
246 * "-p|--pepper"
247 * "-p --pepper"
248 *
249 * Examples:
250 *
251 * // simple boolean defaulting to false
252 * program.option('-p, --pepper', 'add pepper');
253 *
254 * --pepper
255 * program.pepper
256 * // => Boolean
257 *
258 * // simple boolean defaulting to false
259 * program.option('-C, --no-cheese', 'remove cheese');
260 *
261 * program.cheese
262 * // => true
263 *
264 * --no-cheese
265 * program.cheese
266 * // => true
267 *
268 * // required argument
269 * program.option('-C, --chdir <path>', 'change the working directory');
270 *
271 * --chdir /tmp
272 * program.chdir
273 * // => "/tmp"
274 *
275 * // optional argument
276 * program.option('-c, --cheese [type]', 'add cheese [marble]');
277 *
278 * @param {String} flags
279 * @param {String} description
280 * @param {Function|Mixed} fn or default
281 * @param {Mixed} defaultValue
282 * @return {Command} for chaining
283 * @api public
284 */
285
286Command.prototype.option = function(flags, description, fn, defaultValue){
287 var self = this
288 , option = new Option(flags, description)
289 , oname = option.name()
290 , name = camelcase(oname);
291
292 // default as 3rd arg
293 if ('function' != typeof fn) defaultValue = fn, fn = null;
294
295 // preassign default value only for --no-*, [optional], or <required>
296 if (false == option.bool || option.optional || option.required) {
297 // when --no-* we make sure default is true
298 if (false == option.bool) defaultValue = true;
299 // preassign only if we have a default
300 if (undefined !== defaultValue) self[name] = defaultValue;
301 }
302
303 // register the option
304 this.options.push(option);
305
306 // when it's passed assign the value
307 // and conditionally invoke the callback
308 this.on(oname, function(val){
309 // coercion
310 if (null != val && fn) val = fn(val);
311
312 // unassigned or bool
313 if ('boolean' == typeof self[name] || 'undefined' == typeof self[name]) {
314 // if no value, bool true, and we have a default, then use it!
315 if (null == val) {
316 self[name] = option.bool
317 ? defaultValue || true
318 : false;
319 } else {
320 self[name] = val;
321 }
322 } else if (null !== val) {
323 // reassign
324 self[name] = val;
325 }
326 });
327
328 return this;
329};
330
331/**
332 * Parse `argv`, settings options and invoking commands when defined.
333 *
334 * @param {Array} argv
335 * @return {Command} for chaining
336 * @api public
337 */
338
339Command.prototype.parse = function(argv){
340 // store raw args
341 this.rawArgs = argv;
342
343 // guess name
344 if (!this.name) this.name = basename(argv[1]);
345
346 // process argv
347 var parsed = this.parseOptions(this.normalize(argv.slice(2)));
348 this.args = parsed.args;
349 return this.parseArgs(this.args, parsed.unknown);
350};
351
352/**
353 * Normalize `args`, splitting joined short flags. For example
354 * the arg "-abc" is equivalent to "-a -b -c".
355 *
356 * @param {Array} args
357 * @return {Array}
358 * @api private
359 */
360
361Command.prototype.normalize = function(args){
362 var ret = []
363 , arg;
364
365 for (var i = 0, len = args.length; i < len; ++i) {
366 arg = args[i];
367 if (arg.length > 1 && '-' == arg[0] && '-' != arg[1]) {
368 arg.slice(1).split('').forEach(function(c){
369 ret.push('-' + c);
370 });
371 } else {
372 ret.push(arg);
373 }
374 }
375
376 return ret;
377};
378
379/**
380 * Parse command `args`.
381 *
382 * When listener(s) are available those
383 * callbacks are invoked, otherwise the "*"
384 * event is emitted and those actions are invoked.
385 *
386 * @param {Array} args
387 * @return {Command} for chaining
388 * @api private
389 */
390
391Command.prototype.parseArgs = function(args, unknown){
392 var cmds = this.commands
393 , len = cmds.length
394 , name;
395
396 if (args.length) {
397 name = args[0];
398 if (this.listeners(name).length) {
399 this.emit(args.shift(), args, unknown);
400 } else {
401 this.emit('*', args);
402 }
403 } else {
404 outputHelpIfNecessary(this, unknown);
405
406 // If there were no args and we have unknown options,
407 // then they are extraneous and we need to error.
408 if (unknown.length > 0) {
409 this.unknownOption(unknown[0]);
410 }
411 }
412
413 return this;
414};
415
416/**
417 * Return an option matching `arg` if any.
418 *
419 * @param {String} arg
420 * @return {Option}
421 * @api private
422 */
423
424Command.prototype.optionFor = function(arg){
425 for (var i = 0, len = this.options.length; i < len; ++i) {
426 if (this.options[i].is(arg)) {
427 return this.options[i];
428 }
429 }
430};
431
432/**
433 * Parse options from `argv` returning `argv`
434 * void of these options.
435 *
436 * @param {Array} argv
437 * @return {Array}
438 * @api public
439 */
440
441Command.prototype.parseOptions = function(argv){
442 var args = []
443 , len = argv.length
444 , literal
445 , option
446 , arg;
447
448 var unknownOptions = [];
449
450 // parse options
451 for (var i = 0; i < len; ++i) {
452 arg = argv[i];
453
454 // literal args after --
455 if ('--' == arg) {
456 literal = true;
457 continue;
458 }
459
460 if (literal) {
461 args.push(arg);
462 continue;
463 }
464
465 // find matching Option
466 option = this.optionFor(arg);
467
468 // option is defined
469 if (option) {
470 // requires arg
471 if (option.required) {
472 arg = argv[++i];
473 if (null == arg) return this.optionMissingArgument(option);
474 if ('-' == arg[0]) return this.optionMissingArgument(option, arg);
475 this.emit(option.name(), arg);
476 // optional arg
477 } else if (option.optional) {
478 arg = argv[i+1];
479 if (null == arg || '-' == arg[0]) {
480 arg = null;
481 } else {
482 ++i;
483 }
484 this.emit(option.name(), arg);
485 // bool
486 } else {
487 this.emit(option.name());
488 }
489 continue;
490 }
491
492 // looks like an option
493 if (arg.length > 1 && '-' == arg[0]) {
494 unknownOptions.push(arg);
495
496 // If the next argument looks like it might be
497 // an argument for this option, we pass it on.
498 // If it isn't, then it'll simply be ignored
499 if (argv[i+1] && '-' != argv[i+1][0]) {
500 unknownOptions.push(argv[++i]);
501 }
502 continue;
503 }
504
505 // arg
506 args.push(arg);
507 }
508
509 return { args: args, unknown: unknownOptions };
510};
511
512/**
513 * Argument `name` is missing.
514 *
515 * @param {String} name
516 * @api private
517 */
518
519Command.prototype.missingArgument = function(name){
520 console.error();
521 console.error(" error: missing required argument `%s'", name);
522 console.error();
523 process.exit(1);
524};
525
526/**
527 * `Option` is missing an argument, but received `flag` or nothing.
528 *
529 * @param {String} option
530 * @param {String} flag
531 * @api private
532 */
533
534Command.prototype.optionMissingArgument = function(option, flag){
535 console.error();
536 if (flag) {
537 console.error(" error: option `%s' argument missing, got `%s'", option.flags, flag);
538 } else {
539 console.error(" error: option `%s' argument missing", option.flags);
540 }
541 console.error();
542 process.exit(1);
543};
544
545/**
546 * Unknown option `flag`.
547 *
548 * @param {String} flag
549 * @api private
550 */
551
552Command.prototype.unknownOption = function(flag){
553 console.error();
554 console.error(" error: unknown option `%s'", flag);
555 console.error();
556 process.exit(1);
557};
558
559/**
560 * Set the program version to `str`.
561 *
562 * This method auto-registers the "-V, --version" flag
563 * which will print the version number when passed.
564 *
565 * @param {String} str
566 * @param {String} flags
567 * @return {Command} for chaining
568 * @api public
569 */
570
571Command.prototype.version = function(str, flags){
572 if (0 == arguments.length) return this._version;
573 this._version = str;
574 flags = flags || '-V, --version';
575 this.option(flags, 'output the version number');
576 this.on('version', function(){
577 console.log(str);
578 process.exit(0);
579 });
580 return this;
581};
582
583/**
584 * Set the description `str`.
585 *
586 * @param {String} str
587 * @return {String|Command}
588 * @api public
589 */
590
591Command.prototype.description = function(str){
592 if (0 == arguments.length) return this._description;
593 this._description = str;
594 return this;
595};
596
597/**
598 * Set / get the command usage `str`.
599 *
600 * @param {String} str
601 * @return {String|Command}
602 * @api public
603 */
604
605Command.prototype.usage = function(str){
606 var args = this.args.map(function(arg){
607 return arg.required
608 ? '<' + arg.name + '>'
609 : '[' + arg.name + ']';
610 });
611
612 var usage = '[options'
613 + (this.commands.length ? '] [command' : '')
614 + ']'
615 + (this.args.length ? ' ' + args : '');
616 if (0 == arguments.length) return this._usage || usage;
617 this._usage = str;
618
619 return this;
620};
621
622/**
623 * Return the largest option length.
624 *
625 * @return {Number}
626 * @api private
627 */
628
629Command.prototype.largestOptionLength = function(){
630 return this.options.reduce(function(max, option){
631 return Math.max(max, option.flags.length);
632 }, 0);
633};
634
635/**
636 * Return help for options.
637 *
638 * @return {String}
639 * @api private
640 */
641
642Command.prototype.optionHelp = function(){
643 var width = this.largestOptionLength();
644
645 // Prepend the help information
646 return [pad('-h, --help', width) + ' ' + 'output usage information']
647 .concat(this.options.map(function(option){
648 return pad(option.flags, width)
649 + ' ' + option.description;
650 }))
651 .join('\n');
652};
653
654/**
655 * Return command help documentation.
656 *
657 * @return {String}
658 * @api private
659 */
660
661Command.prototype.commandHelp = function(){
662 if (!this.commands.length) return '';
663 return [
664 ''
665 , ' Commands:'
666 , ''
667 , this.commands.map(function(cmd){
668 var args = cmd.args.map(function(arg){
669 return arg.required
670 ? '<' + arg.name + '>'
671 : '[' + arg.name + ']';
672 }).join(' ');
673
674 return cmd.name
675 + (cmd.options.length
676 ? ' [options]'
677 : '') + ' ' + args
678 + (cmd.description()
679 ? '\n' + cmd.description()
680 : '');
681 }).join('\n\n').replace(/^/gm, ' ')
682 , ''
683 ].join('\n');
684};
685
686/**
687 * Return program help documentation.
688 *
689 * @return {String}
690 * @api private
691 */
692
693Command.prototype.helpInformation = function(){
694 return [
695 ''
696 , ' Usage: ' + this.name + ' ' + this.usage()
697 , '' + this.commandHelp()
698 , ' Options:'
699 , ''
700 , '' + this.optionHelp().replace(/^/gm, ' ')
701 , ''
702 , ''
703 ].join('\n');
704};
705
706/**
707 * Prompt for a `Number`.
708 *
709 * @param {String} str
710 * @param {Function} fn
711 * @api private
712 */
713
714Command.prototype.promptForNumber = function(str, fn){
715 var self = this;
716 this.promptSingleLine(str, function parseNumber(val){
717 val = Number(val);
718 if (isNaN(val)) return self.promptSingleLine(str + '(must be a number) ', parseNumber);
719 fn(val);
720 });
721};
722
723/**
724 * Prompt for a `Date`.
725 *
726 * @param {String} str
727 * @param {Function} fn
728 * @api private
729 */
730
731Command.prototype.promptForDate = function(str, fn){
732 var self = this;
733 this.promptSingleLine(str, function parseDate(val){
734 val = new Date(val);
735 if (isNaN(val.getTime())) return self.promptSingleLine(str + '(must be a date) ', parseDate);
736 fn(val);
737 });
738};
739
740/**
741 * Single-line prompt.
742 *
743 * @param {String} str
744 * @param {Function} fn
745 * @api private
746 */
747
748Command.prototype.promptSingleLine = function(str, fn){
749 if ('function' == typeof arguments[2]) {
750 return this['promptFor' + (fn.name || fn)](str, arguments[2]);
751 }
752
753 process.stdout.write(str);
754 process.stdin.setEncoding('utf8');
755 process.stdin.once('data', function(val){
756 fn(val.trim());
757 }).resume();
758};
759
760/**
761 * Multi-line prompt.
762 *
763 * @param {String} str
764 * @param {Function} fn
765 * @api private
766 */
767
768Command.prototype.promptMultiLine = function(str, fn){
769 var buf = [];
770 console.log(str);
771 process.stdin.setEncoding('utf8');
772 process.stdin.on('data', function(val){
773 if ('\n' == val || '\r\n' == val) {
774 process.stdin.removeAllListeners('data');
775 fn(buf.join('\n'));
776 } else {
777 buf.push(val.trimRight());
778 }
779 }).resume();
780};
781
782/**
783 * Prompt `str` and callback `fn(val)`
784 *
785 * Commander supports single-line and multi-line prompts.
786 * To issue a single-line prompt simply add white-space
787 * to the end of `str`, something like "name: ", whereas
788 * for a multi-line prompt omit this "description:".
789 *
790 *
791 * Examples:
792 *
793 * program.prompt('Username: ', function(name){
794 * console.log('hi %s', name);
795 * });
796 *
797 * program.prompt('Description:', function(desc){
798 * console.log('description was "%s"', desc.trim());
799 * });
800 *
801 * @param {String|Object} str
802 * @param {Function} fn
803 * @api public
804 */
805
806Command.prototype.prompt = function(str, fn){
807 var self = this;
808
809 if ('string' == typeof str) {
810 if (/ $/.test(str)) return this.promptSingleLine.apply(this, arguments);
811 this.promptMultiLine(str, fn);
812 } else {
813 var keys = Object.keys(str)
814 , obj = {};
815
816 function next() {
817 var key = keys.shift()
818 , label = str[key];
819
820 if (!key) return fn(obj);
821 self.prompt(label, function(val){
822 obj[key] = val;
823 next();
824 });
825 }
826
827 next();
828 }
829};
830
831/**
832 * Prompt for password with `str`, `mask` char and callback `fn(val)`.
833 *
834 * The mask string defaults to '', aka no output is
835 * written while typing, you may want to use "*" etc.
836 *
837 * Examples:
838 *
839 * program.password('Password: ', function(pass){
840 * console.log('got "%s"', pass);
841 * process.stdin.destroy();
842 * });
843 *
844 * program.password('Password: ', '*', function(pass){
845 * console.log('got "%s"', pass);
846 * process.stdin.destroy();
847 * });
848 *
849 * @param {String} str
850 * @param {String} mask
851 * @param {Function} fn
852 * @api public
853 */
854
855Command.prototype.password = function(str, mask, fn){
856 var self = this
857 , buf = '';
858
859 // default mask
860 if ('function' == typeof mask) {
861 fn = mask;
862 mask = '';
863 }
864
865 process.stdin.resume();
866 tty.setRawMode(true);
867 process.stdout.write(str);
868
869 // keypress
870 process.stdin.on('keypress', function(c, key){
871 if (key && 'enter' == key.name) {
872 console.log();
873 process.stdin.removeAllListeners('keypress');
874 tty.setRawMode(false);
875 if (!buf.trim().length) return self.password(str, mask, fn);
876 fn(buf);
877 return;
878 }
879
880 if (key && key.ctrl && 'c' == key.name) {
881 console.log('%s', buf);
882 process.exit();
883 }
884
885 process.stdout.write(mask);
886 buf += c;
887 }).resume();
888};
889
890/**
891 * Confirmation prompt with `str` and callback `fn(bool)`
892 *
893 * Examples:
894 *
895 * program.confirm('continue? ', function(ok){
896 * console.log(' got %j', ok);
897 * process.stdin.destroy();
898 * });
899 *
900 * @param {String} str
901 * @param {Function} fn
902 * @api public
903 */
904
905
906Command.prototype.confirm = function(str, fn, verbose){
907 var self = this;
908 this.prompt(str, function(ok){
909 if (!ok.trim()) {
910 if (!verbose) str += '(yes or no) ';
911 return self.confirm(str, fn, true);
912 }
913 fn(parseBool(ok));
914 });
915};
916
917/**
918 * Choice prompt with `list` of items and callback `fn(index, item)`
919 *
920 * Examples:
921 *
922 * var list = ['tobi', 'loki', 'jane', 'manny', 'luna'];
923 *
924 * console.log('Choose the coolest pet:');
925 * program.choose(list, function(i){
926 * console.log('you chose %d "%s"', i, list[i]);
927 * process.stdin.destroy();
928 * });
929 *
930 * @param {Array} list
931 * @param {Number|Function} index or fn
932 * @param {Function} fn
933 * @api public
934 */
935
936Command.prototype.choose = function(list, index, fn){
937 var self = this
938 , hasDefault = 'number' == typeof index;
939
940 if (!hasDefault) {
941 fn = index;
942 index = null;
943 }
944
945 list.forEach(function(item, i){
946 if (hasDefault && i == index) {
947 console.log('* %d) %s', i + 1, item);
948 } else {
949 console.log(' %d) %s', i + 1, item);
950 }
951 });
952
953 function again() {
954 self.prompt(' : ', function(val){
955 val = parseInt(val, 10) - 1;
956 if (hasDefault && isNaN(val)) val = index;
957
958 if (null == list[val]) {
959 again();
960 } else {
961 fn(val, list[val]);
962 }
963 });
964 }
965
966 again();
967};
968
969/**
970 * Camel-case the given `flag`
971 *
972 * @param {String} flag
973 * @return {String}
974 * @api private
975 */
976
977function camelcase(flag) {
978 return flag.split('-').reduce(function(str, word){
979 return str + word[0].toUpperCase() + word.slice(1);
980 });
981}
982
983/**
984 * Parse a boolean `str`.
985 *
986 * @param {String} str
987 * @return {Boolean}
988 * @api private
989 */
990
991function parseBool(str) {
992 return /^y|yes|ok|true$/i.test(str);
993}
994
995/**
996 * Pad `str` to `width`.
997 *
998 * @param {String} str
999 * @param {Number} width
1000 * @return {String}
1001 * @api private
1002 */
1003
1004function pad(str, width) {
1005 var len = Math.max(0, width - str.length);
1006 return str + Array(len + 1).join(' ');
1007}
1008
1009/**
1010 * Output help information if necessary
1011 *
1012 * @param {Command} command to output help for
1013 * @param {Array} array of options to search for -h or --help
1014 * @api private
1015 */
1016
1017function outputHelpIfNecessary(cmd, options) {
1018 options = options || [];
1019 for (var i = 0; i < options.length; i++) {
1020 if (options[i] == '--help' || options[i] == '-h') {
1021 process.stdout.write(cmd.helpInformation());
1022 cmd.emit('--help');
1023 process.exit(0);
1024 }
1025 }
1026}