commander 2.7.0 → 2.9.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.
Files changed (4) hide show
  1. package/History.md +28 -1
  2. package/Readme.md +47 -7
  3. package/index.js +98 -39
  4. package/package.json +3 -2
package/History.md CHANGED
@@ -1,5 +1,32 @@
1
1
 
2
- 2.7.0 / 2015-03-07
2
+ 2.9.0 / 2015-10-13
3
+ ==================
4
+
5
+ * Add option `isDefault` to set default subcommand #415 @Qix-
6
+ * Add callback to allow filtering or post-processing of help text #434 @djulien
7
+ * Fix `undefined` text in help information close #414 #416 @zhiyelee
8
+
9
+ 2.8.1 / 2015-04-22
10
+ ==================
11
+
12
+ * Back out `support multiline description` Close #396 #397
13
+
14
+ 2.8.0 / 2015-04-07
15
+ ==================
16
+
17
+ * Add `process.execArg` support, execution args like `--harmony` will be passed to sub-commands #387 @DigitalIO @zhiyelee
18
+ * Fix bug in Git-style sub-commands #372 @zhiyelee
19
+ * Allow commands to be hidden from help #383 @tonylukasavage
20
+ * When git-style sub-commands are in use, yet none are called, display help #382 @claylo
21
+ * Add ability to specify arguments syntax for top-level command #258 @rrthomas
22
+ * Support multiline descriptions #208 @zxqfox
23
+
24
+ 2.7.1 / 2015-03-11
25
+ ==================
26
+
27
+ * Revert #347 (fix collisions when option and first arg have same name) which causes a bug in #367.
28
+
29
+ 2.7.0 / 2015-03-09
3
30
  ==================
4
31
 
5
32
  * Fix git-style bug when installed globally. Close #335 #349 @zhiyelee
package/Readme.md CHANGED
@@ -31,14 +31,14 @@ program
31
31
  .version('0.0.1')
32
32
  .option('-p, --peppers', 'Add peppers')
33
33
  .option('-P, --pineapple', 'Add pineapple')
34
- .option('-b, --bbq', 'Add bbq sauce')
34
+ .option('-b, --bbq-sauce', 'Add bbq sauce')
35
35
  .option('-c, --cheese [type]', 'Add the specified type of cheese [marble]', 'marble')
36
36
  .parse(process.argv);
37
37
 
38
38
  console.log('you ordered a pizza with:');
39
39
  if (program.peppers) console.log(' - peppers');
40
40
  if (program.pineapple) console.log(' - pineapple');
41
- if (program.bbq) console.log(' - bbq');
41
+ if (program.bbqSauce) console.log(' - bbq');
42
42
  console.log(' - %s cheese', program.cheese);
43
43
  ```
44
44
 
@@ -132,6 +132,31 @@ program.parse(process.argv);
132
132
  An `Array` is used for the value of a variadic argument. This applies to `program.args` as well as the argument passed
133
133
  to your action as demonstrated above.
134
134
 
135
+ ## Specify the argument syntax
136
+
137
+ ```js
138
+ #!/usr/bin/env node
139
+
140
+ var program = require('../');
141
+
142
+ program
143
+ .version('0.0.1')
144
+ .arguments('<cmd> [env]')
145
+ .action(function (cmd, env) {
146
+ cmdValue = cmd;
147
+ envValue = env;
148
+ });
149
+
150
+ program.parse(process.argv);
151
+
152
+ if (typeof cmdValue === 'undefined') {
153
+ console.error('no command given!');
154
+ process.exit(1);
155
+ }
156
+ console.log('command:', cmdValue);
157
+ console.log('environment:', envValue || "no environment given");
158
+ ```
159
+
135
160
  ## Git-style sub-commands
136
161
 
137
162
  ```js
@@ -142,14 +167,22 @@ program
142
167
  .version('0.0.1')
143
168
  .command('install [name]', 'install one or more packages')
144
169
  .command('search [query]', 'search with optional query')
145
- .command('list', 'list packages installed')
170
+ .command('list', 'list packages installed', {isDefault: true})
146
171
  .parse(process.argv);
147
172
  ```
148
173
 
149
174
  When `.command()` is invoked with a description argument, no `.action(callback)` should be called to handle sub-commands, otherwise there will be an error. This tells commander that you're going to use separate executables for sub-commands, much like `git(1)` and other popular tools.
150
175
  The commander will try to search the executables in the directory of the entry script (like `./examples/pm`) with the name `program-command`, like `pm-install`, `pm-search`.
151
176
 
152
- If the program is designed to installed globally, make sure the executables have proper modes, like `755`.
177
+ Options can be passed with the call to `.command()`. Specifying `true` for `opts.noHelp` will remove the option from the generated help output. Specifying `true` for `opts.isDefault` will run the subcommand if no other subcommand is specified.
178
+
179
+ If the program is designed to be installed globally, make sure the executables have proper modes, like `755`.
180
+
181
+ ### `--harmony`
182
+
183
+ You can enable `--harmony` option in two ways:
184
+ * Use `#! /usr/bin/env node --harmony` in the sub-commands scripts. Note some os version don’t support this pattern.
185
+ * Use the `--harmony` option when call the command, like `node --harmony examples/pm publish`. The `--harmony` option will be preserved when spawning sub-command process.
153
186
 
154
187
  ## Automated --help
155
188
 
@@ -235,14 +268,16 @@ Examples:
235
268
 
236
269
  ```
237
270
 
238
- ## .outputHelp()
271
+ ## .outputHelp(cb)
239
272
 
240
273
  Output help information without exiting.
274
+ Optional callback cb allows post-processing of help text before it is displayed.
241
275
 
242
276
  If you want to display help by default (e.g. if no command was provided), you can use something like:
243
277
 
244
278
  ```js
245
279
  var program = require('commander');
280
+ var colors = require('colors');
246
281
 
247
282
  program
248
283
  .version('0.0.1')
@@ -250,13 +285,18 @@ program
250
285
  .parse(process.argv);
251
286
 
252
287
  if (!process.argv.slice(2).length) {
253
- program.outputHelp();
288
+ program.outputHelp(make_red);
254
289
  }
290
+
291
+ function make_red(txt) {
292
+ return colors.red(txt); //display the help text in red on the console
293
+ }
255
294
  ```
256
295
 
257
- ## .help()
296
+ ## .help(cb)
258
297
 
259
298
  Output help information and exit immediately.
299
+ Optional callback cb allows post-processing of help text before it is displayed.
260
300
 
261
301
  ## Examples
262
302
 
package/index.js CHANGED
@@ -1,4 +1,3 @@
1
-
2
1
  /**
3
2
  * Module dependencies.
4
3
  */
@@ -83,10 +82,10 @@ Option.prototype.is = function(arg) {
83
82
  function Command(name) {
84
83
  this.commands = [];
85
84
  this.options = [];
86
- this._execs = [];
85
+ this._execs = {};
87
86
  this._allowUnknownOption = false;
88
87
  this._args = [];
89
- this._name = name;
88
+ this._name = name || '';
90
89
  }
91
90
 
92
91
  /**
@@ -156,7 +155,8 @@ Command.prototype.__proto__ = EventEmitter.prototype;
156
155
  * @api public
157
156
  */
158
157
 
159
- Command.prototype.command = function(name, desc) {
158
+ Command.prototype.command = function(name, desc, opts) {
159
+ opts = opts || {};
160
160
  var args = name.split(/ +/);
161
161
  var cmd = new Command(args.shift());
162
162
 
@@ -164,8 +164,10 @@ Command.prototype.command = function(name, desc) {
164
164
  cmd.description(desc);
165
165
  this.executables = true;
166
166
  this._execs[cmd._name] = true;
167
+ if (opts.isDefault) this.defaultExecutable = cmd._name;
167
168
  }
168
169
 
170
+ cmd._noHelp = !!opts.noHelp;
169
171
  this.commands.push(cmd);
170
172
  cmd.parseExpectedArgs(args);
171
173
  cmd.parent = this;
@@ -174,6 +176,16 @@ Command.prototype.command = function(name, desc) {
174
176
  return cmd;
175
177
  };
176
178
 
179
+ /**
180
+ * Define argument syntax for the top-level command.
181
+ *
182
+ * @api public
183
+ */
184
+
185
+ Command.prototype.arguments = function (desc) {
186
+ return this.parseExpectedArgs(desc.split(/ +/));
187
+ };
188
+
177
189
  /**
178
190
  * Add an implicit `help [cmd]` subcommand
179
191
  * which invokes `--help` for the given command.
@@ -288,8 +300,10 @@ Command.prototype.action = function(fn) {
288
300
 
289
301
  fn.apply(self, args);
290
302
  };
291
- this.parent.on('action_' + this._name, listener);
292
- if (this._alias) this.parent.on('action_' + this._alias, listener);
303
+ var parent = this.parent || this;
304
+ var name = parent === this ? '*' : this._name;
305
+ parent.on(name, listener);
306
+ if (this._alias) parent.on(this._alias, listener);
293
307
  return this;
294
308
  };
295
309
 
@@ -431,6 +445,12 @@ Command.prototype.parse = function(argv) {
431
445
  // guess name
432
446
  this._name = this._name || basename(argv[1], '.js');
433
447
 
448
+ // github-style sub-commands with no sub-command
449
+ if (this.executables && argv.length < 3 && !this.defaultExecutable) {
450
+ // this user needs help
451
+ argv.push('--help');
452
+ }
453
+
434
454
  // process argv
435
455
  var parsed = this.parseOptions(this.normalize(argv.slice(2)));
436
456
  var args = this.args = parsed.args;
@@ -441,6 +461,10 @@ Command.prototype.parse = function(argv) {
441
461
  var name = result.args[0];
442
462
  if (this._execs[name] && typeof this._execs[name] != "function") {
443
463
  return this.executeSubCommand(argv, args, parsed.unknown);
464
+ } else if (this.defaultExecutable) {
465
+ // use the default subcommand
466
+ args.unshift(name = this.defaultExecutable);
467
+ return this.executeSubCommand(argv, args, parsed.unknown);
444
468
  }
445
469
 
446
470
  return result;
@@ -469,30 +493,48 @@ Command.prototype.executeSubCommand = function(argv, args, unknown) {
469
493
 
470
494
  // executable
471
495
  var f = argv[1];
472
- var link = readlink(f);
496
+ // name of the subcommand, link `pm-install`
497
+ var bin = basename(f, '.js') + '-' + args[0];
498
+
499
+
500
+ // In case of globally installed, get the base dir where executable
501
+ // subcommand file should be located at
502
+ var baseDir
503
+ , link = readlink(f);
504
+
505
+ // when symbolink is relative path
473
506
  if (link !== f && link.charAt(0) !== '/') {
474
507
  link = path.join(dirname(f), link)
475
508
  }
476
- var dir = dirname(link);
477
- var bin = basename(f, '.js') + '-' + args[0];
509
+ baseDir = dirname(link);
478
510
 
479
511
  // prefer local `./<bin>` to bin in the $PATH
480
- var local = path.join(dir, bin);
481
- try {
482
- // for versions before node v0.8 when there weren't `fs.existsSync`
483
- if (fs.statSync(local).isFile()) {
484
- bin = local;
485
- }
486
- } catch (e) {}
512
+ var localBin = path.join(baseDir, bin);
513
+
514
+ // whether bin file is a js script with explicit `.js` extension
515
+ var isExplicitJS = false;
516
+ if (exists(localBin + '.js')) {
517
+ bin = localBin + '.js';
518
+ isExplicitJS = true;
519
+ } else if (exists(localBin)) {
520
+ bin = localBin;
521
+ }
487
522
 
488
- // run it
489
523
  args = args.slice(1);
490
524
 
491
525
  var proc;
492
526
  if (process.platform !== 'win32') {
493
- proc = spawn(bin, args, { stdio: 'inherit', customFds: [0, 1, 2] });
527
+ if (isExplicitJS) {
528
+ args.unshift(localBin);
529
+ // add executable arguments to spawn
530
+ args = (process.execArgv || []).concat(args);
531
+
532
+ proc = spawn('node', args, { stdio: 'inherit', customFds: [0, 1, 2] });
533
+ } else {
534
+ proc = spawn(bin, args, { stdio: 'inherit', customFds: [0, 1, 2] });
535
+ }
494
536
  } else {
495
- args.unshift(local);
537
+ args.unshift(localBin);
496
538
  proc = spawn(process.execPath, args, { stdio: 'inherit'});
497
539
  }
498
540
 
@@ -506,6 +548,7 @@ Command.prototype.executeSubCommand = function(argv, args, unknown) {
506
548
  process.exit(1);
507
549
  });
508
550
 
551
+ // Store the reference to the child process
509
552
  this.runningCommand = proc;
510
553
  };
511
554
 
@@ -568,8 +611,8 @@ Command.prototype.parseArgs = function(args, unknown) {
568
611
 
569
612
  if (args.length) {
570
613
  name = args[0];
571
- if (this.listeners('action_' + name).length) {
572
- this.emit('action_' + args.shift(), args, unknown);
614
+ if (this.listeners(name).length) {
615
+ this.emit(args.shift(), args, unknown);
573
616
  } else {
574
617
  this.emit('*', args);
575
618
  }
@@ -793,7 +836,7 @@ Command.prototype.version = function(str, flags) {
793
836
  */
794
837
 
795
838
  Command.prototype.description = function(str) {
796
- if (0 == arguments.length) return this._description;
839
+ if (0 === arguments.length) return this._description;
797
840
  this._description = str;
798
841
  return this;
799
842
  };
@@ -872,10 +915,10 @@ Command.prototype.optionHelp = function() {
872
915
 
873
916
  // Prepend the help information
874
917
  return [pad('-h, --help', width) + ' ' + 'output usage information']
875
- .concat(this.options.map(function(option) {
876
- return pad(option.flags, width) + ' ' + option.description;
918
+ .concat(this.options.map(function(option) {
919
+ return pad(option.flags, width) + ' ' + option.description;
877
920
  }))
878
- .join('\n');
921
+ .join('\n');
879
922
  };
880
923
 
881
924
  /**
@@ -888,21 +931,19 @@ Command.prototype.optionHelp = function() {
888
931
  Command.prototype.commandHelp = function() {
889
932
  if (!this.commands.length) return '';
890
933
 
891
- var commands = this.commands.map(function(cmd) {
934
+ var commands = this.commands.filter(function(cmd) {
935
+ return !cmd._noHelp;
936
+ }).map(function(cmd) {
892
937
  var args = cmd._args.map(function(arg) {
893
938
  return humanReadableArgName(arg);
894
939
  }).join(' ');
895
940
 
896
941
  return [
897
942
  cmd._name
898
- + (cmd._alias
899
- ? '|' + cmd._alias
900
- : '')
901
- + (cmd.options.length
902
- ? ' [options]'
903
- : '')
943
+ + (cmd._alias ? '|' + cmd._alias : '')
944
+ + (cmd.options.length ? ' [options]' : '')
904
945
  + ' ' + args
905
- , cmd.description()
946
+ , cmd.description()
906
947
  ];
907
948
  });
908
949
 
@@ -911,11 +952,12 @@ Command.prototype.commandHelp = function() {
911
952
  }, 0);
912
953
 
913
954
  return [
914
- ''
955
+ ''
915
956
  , ' Commands:'
916
957
  , ''
917
958
  , commands.map(function(cmd) {
918
- return pad(cmd[0], width) + ' ' + cmd[1];
959
+ var desc = cmd[1] ? ' ' + cmd[1] : '';
960
+ return pad(cmd[0], width) + desc;
919
961
  }).join('\n').replace(/^/gm, ' ')
920
962
  , ''
921
963
  ].join('\n');
@@ -972,8 +1014,13 @@ Command.prototype.helpInformation = function() {
972
1014
  * @api public
973
1015
  */
974
1016
 
975
- Command.prototype.outputHelp = function() {
976
- process.stdout.write(this.helpInformation());
1017
+ Command.prototype.outputHelp = function(cb) {
1018
+ if (!cb) {
1019
+ cb = function(passthru) {
1020
+ return passthru;
1021
+ }
1022
+ }
1023
+ process.stdout.write(cb(this.helpInformation()));
977
1024
  this.emit('--help');
978
1025
  };
979
1026
 
@@ -983,8 +1030,8 @@ Command.prototype.outputHelp = function() {
983
1030
  * @api public
984
1031
  */
985
1032
 
986
- Command.prototype.help = function() {
987
- this.outputHelp();
1033
+ Command.prototype.help = function(cb) {
1034
+ this.outputHelp(cb);
988
1035
  process.exit();
989
1036
  };
990
1037
 
@@ -1049,3 +1096,15 @@ function humanReadableArgName(arg) {
1049
1096
  ? '<' + nameOutput + '>'
1050
1097
  : '[' + nameOutput + ']'
1051
1098
  }
1099
+
1100
+ // for versions before node v0.8 when there weren't `fs.existsSync`
1101
+ function exists(file) {
1102
+ try {
1103
+ if (fs.statSync(file).isFile()) {
1104
+ return true;
1105
+ }
1106
+ } catch (e) {
1107
+ return false;
1108
+ }
1109
+ }
1110
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "commander",
3
- "version": "2.7.0",
3
+ "version": "2.9.0",
4
4
  "description": "the complete solution for node.js command-line programs",
5
5
  "keywords": [
6
6
  "command",
@@ -14,7 +14,8 @@
14
14
  "url": "https://github.com/tj/commander.js.git"
15
15
  },
16
16
  "devDependencies": {
17
- "should": ">= 0.0.1"
17
+ "should": ">= 0.0.1",
18
+ "sinon": ">=1.17.1"
18
19
  },
19
20
  "scripts": {
20
21
  "test": "make test"