commander 1.0.4 → 1.2.0

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