commander 7.0.0 → 7.1.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/CHANGELOG.md CHANGED
@@ -8,6 +8,26 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
8
8
  <!-- markdownlint-disable MD024 -->
9
9
  <!-- markdownlint-disable MD004 -->
10
10
 
11
+ ## [7.1.0] (2021-02-15)
12
+
13
+ ### Added
14
+
15
+ - support for named imports from ECMAScript modules ([#1440])
16
+ - add `.cjs` to list of expected script file extensions ([#1449])
17
+ - allow using option choices and variadic together ([#1454])
18
+
19
+ ### Fixed
20
+
21
+ - replace use of deprecated `process.mainModule` ([#1448])
22
+ - regression for legacy `command('*')` and call when command line includes options ([#1464])
23
+ - regression for `on('command:*', ...)` and call when command line includes unknown options ([#1464])
24
+ - display best error for combination of unknown command and unknown option (i.e. unknown command) ([#1464])
25
+
26
+ ### Changed
27
+
28
+ - make TypeScript typings tests stricter ([#1453])
29
+ - improvements to README and tests
30
+
11
31
  ## [7.0.0] (2021-01-15)
12
32
 
13
33
  ### Added
@@ -53,7 +73,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
53
73
  - *Breaking:* `.passCommandToAction()` ([#1409])
54
74
  - no longer needed as action handler is passed options and command
55
75
  - *Breaking:* "extra arguments" parameter to action handler ([#1409])
56
- - if being used to detect excess arguments, there is now an error displayed by default
76
+ - if being used to detect excess arguments, there is now an error available by setting `.allowExcessArguments(false)`
57
77
 
58
78
  ### Migration Tips
59
79
 
@@ -376,8 +396,17 @@ to expand `-fb` to `-f -b` rather than `-f b`.
376
396
  [#1403]: https://github.com/tj/commander.js/pull/1403
377
397
  [#1409]: https://github.com/tj/commander.js/pull/1409
378
398
  [#1427]: https://github.com/tj/commander.js/pull/1427
399
+ [#1440]: https://github.com/tj/commander.js/pull/1440
400
+ [#1448]: https://github.com/tj/commander.js/pull/1448
401
+ [#1449]: https://github.com/tj/commander.js/pull/1449
402
+ [#1453]: https://github.com/tj/commander.js/pull/1453
403
+ [#1454]: https://github.com/tj/commander.js/pull/1454
404
+ [#1464]: https://github.com/tj/commander.js/pull/1464
405
+
406
+
379
407
 
380
408
  [Unreleased]: https://github.com/tj/commander.js/compare/master...develop
409
+ [7.1.0]: https://github.com/tj/commander.js/compare/v7.0.0...v7.1.0
381
410
  [7.0.0]: https://github.com/tj/commander.js/compare/v6.2.1...v7.0.0
382
411
  [7.0.0-2]: https://github.com/tj/commander.js/compare/v7.0.0-1...v7.0.0-2
383
412
  [7.0.0-1]: https://github.com/tj/commander.js/compare/v7.0.0-0...v7.0.0-1
package/Readme.md CHANGED
@@ -39,7 +39,6 @@ Read this in other languages: English | [简体中文](./Readme_zh-CN.md)
39
39
  - [Legacy options as properties](#legacy-options-as-properties)
40
40
  - [TypeScript](#typescript)
41
41
  - [createCommand()](#createcommand)
42
- - [Import into ECMAScript Module](#import-into-ecmascript-module)
43
42
  - [Node options such as `--harmony`](#node-options-such-as---harmony)
44
43
  - [Debugging stand-alone executable subcommands](#debugging-stand-alone-executable-subcommands)
45
44
  - [Override exit and output handling](#override-exit-and-output-handling)
@@ -74,6 +73,23 @@ const program = new Command();
74
73
  program.version('0.0.1');
75
74
  ```
76
75
 
76
+ For named imports in ECMAScript modules, import from `commander/esm.mjs`.
77
+
78
+ ```js
79
+ // index.mjs
80
+ import { Command } from 'commander/esm.mjs';
81
+ const program = new Command();
82
+ ```
83
+
84
+ And in TypeScript:
85
+
86
+ ```ts
87
+ // index.ts
88
+ import { Command } from 'commander';
89
+ const program = new Command();
90
+ ```
91
+
92
+
77
93
  ## Options
78
94
 
79
95
  Options are defined with the `.option()` method, also serving as documentation for the options. Each option can have a short flag (single character) and a long name, separated by a comma or space or vertical bar ('|').
@@ -655,7 +671,7 @@ You can execute custom actions by listening to command and option events.
655
671
 
656
672
  ```js
657
673
  program.on('option:verbose', function () {
658
- process.env.VERBOSE = this.verbose;
674
+ process.env.VERBOSE = this.opts().verbose;
659
675
  });
660
676
 
661
677
  program.on('command:*', function (operands) {
@@ -740,8 +756,6 @@ program
740
756
 
741
757
  ### TypeScript
742
758
 
743
- The Commander package includes its TypeScript Definition file.
744
-
745
759
  If you use `ts-node` and stand-alone executable subcommands written as `.ts` files, you need to call your program through node to get the subcommands called correctly. e.g.
746
760
 
747
761
  ```bash
@@ -761,17 +775,6 @@ const program = createCommand();
761
775
  when creating subcommands using `.command()`, and you may override it to
762
776
  customise the new subcommand (example file [custom-command-class.js](./examples/custom-command-class.js)).
763
777
 
764
- ### Import into ECMAScript Module
765
-
766
- Commander is currently a CommonJS package, and the default export can be imported into an ES Module:
767
-
768
- ```js
769
- // index.mjs
770
- import commander from 'commander';
771
- const program = commander.program;
772
- const newCommand = new commander.Command();
773
- ```
774
-
775
778
  ### Node options such as `--harmony`
776
779
 
777
780
  You can enable `--harmony` option in two ways:
package/esm.mjs ADDED
@@ -0,0 +1,4 @@
1
+ import commander from './index.js';
2
+
3
+ // wrapper to provide named exports for ESM.
4
+ export const { program, Option, Command, CommanderError, InvalidOptionArgumentError, Help, createCommand } = commander;
package/index.js CHANGED
@@ -425,6 +425,18 @@ class Option {
425
425
  return this;
426
426
  };
427
427
 
428
+ /**
429
+ * @api private
430
+ */
431
+
432
+ _concatValue(value, previous) {
433
+ if (previous === this.defaultValue || !Array.isArray(previous)) {
434
+ return [value];
435
+ }
436
+
437
+ return previous.concat(value);
438
+ }
439
+
428
440
  /**
429
441
  * Only allow option value to be one of choices.
430
442
  *
@@ -434,10 +446,13 @@ class Option {
434
446
 
435
447
  choices(values) {
436
448
  this.argChoices = values;
437
- this.parseArg = (arg) => {
449
+ this.parseArg = (arg, previous) => {
438
450
  if (!values.includes(arg)) {
439
451
  throw new InvalidOptionArgumentError(`Allowed choices are ${values.join(', ')}.`);
440
452
  }
453
+ if (this.variadic) {
454
+ return this._concatValue(arg, previous);
455
+ }
441
456
  return arg;
442
457
  };
443
458
  return this;
@@ -976,11 +991,7 @@ class Command extends EventEmitter {
976
991
  throw err;
977
992
  }
978
993
  } else if (val !== null && option.variadic) {
979
- if (oldValue === defaultValue || !Array.isArray(oldValue)) {
980
- val = [val];
981
- } else {
982
- val = oldValue.concat(val);
983
- }
994
+ val = option._concatValue(val, oldValue);
984
995
  }
985
996
 
986
997
  // unassigned or boolean value
@@ -1270,10 +1281,8 @@ class Command extends EventEmitter {
1270
1281
  default:
1271
1282
  throw new Error(`unexpected parse option { from: '${parseOptions.from}' }`);
1272
1283
  }
1273
- // @ts-ignore: unknown property
1274
- if (!this._scriptPath && process.mainModule) {
1275
- // @ts-ignore: unknown property
1276
- this._scriptPath = process.mainModule.filename;
1284
+ if (!this._scriptPath && require.main) {
1285
+ this._scriptPath = require.main.filename;
1277
1286
  }
1278
1287
 
1279
1288
  // Guess name, used in usage in help.
@@ -1319,7 +1328,7 @@ class Command extends EventEmitter {
1319
1328
  _executeSubCommand(subcommand, args) {
1320
1329
  args = args.slice();
1321
1330
  let launchWithNode = false; // Use node for source targets so do not need to get permissions correct, and on Windows.
1322
- const sourceExt = ['.js', '.ts', '.tsx', '.mjs'];
1331
+ const sourceExt = ['.js', '.ts', '.tsx', '.mjs', '.cjs'];
1323
1332
 
1324
1333
  // Not checking for help first. Unlikely to have mandatory and executable, and can't robustly test for help flags in external command.
1325
1334
  this._checkForMissingMandatoryOptions();
@@ -1327,10 +1336,8 @@ class Command extends EventEmitter {
1327
1336
  // Want the entry script as the reference for command name and directory for searching for other files.
1328
1337
  let scriptPath = this._scriptPath;
1329
1338
  // Fallback in case not set, due to how Command created or called.
1330
- // @ts-ignore: unknown property
1331
- if (!scriptPath && process.mainModule) {
1332
- // @ts-ignore: unknown property
1333
- scriptPath = process.mainModule.filename;
1339
+ if (!scriptPath && require.main) {
1340
+ scriptPath = require.main.filename;
1334
1341
  }
1335
1342
 
1336
1343
  let baseDir;
@@ -1468,12 +1475,17 @@ class Command extends EventEmitter {
1468
1475
 
1469
1476
  outputHelpIfRequested(this, parsed.unknown);
1470
1477
  this._checkForMissingMandatoryOptions();
1471
- if (parsed.unknown.length > 0) {
1472
- this.unknownOption(parsed.unknown[0]);
1473
- }
1478
+
1479
+ // We do not always call this check to avoid masking a "better" error, like unknown command.
1480
+ const checkForUnknownOptions = () => {
1481
+ if (parsed.unknown.length > 0) {
1482
+ this.unknownOption(parsed.unknown[0]);
1483
+ }
1484
+ };
1474
1485
 
1475
1486
  const commandEvent = `command:${this.name()}`;
1476
1487
  if (this._actionHandler) {
1488
+ checkForUnknownOptions();
1477
1489
  // Check expected arguments and collect variadic together.
1478
1490
  const args = this.args.slice();
1479
1491
  this._args.forEach((arg, i) => {
@@ -1491,19 +1503,24 @@ class Command extends EventEmitter {
1491
1503
  this._actionHandler(args);
1492
1504
  if (this.parent) this.parent.emit(commandEvent, operands, unknown); // legacy
1493
1505
  } else if (this.parent && this.parent.listenerCount(commandEvent)) {
1506
+ checkForUnknownOptions();
1494
1507
  this.parent.emit(commandEvent, operands, unknown); // legacy
1495
1508
  } else if (operands.length) {
1496
- if (this._findCommand('*')) { // legacy
1509
+ if (this._findCommand('*')) { // legacy default command
1497
1510
  this._dispatchSubcommand('*', operands, unknown);
1498
1511
  } else if (this.listenerCount('command:*')) {
1512
+ // skip option check, emit event for possible misspelling suggestion
1499
1513
  this.emit('command:*', operands, unknown);
1500
1514
  } else if (this.commands.length) {
1501
1515
  this.unknownCommand();
1516
+ } else {
1517
+ checkForUnknownOptions();
1502
1518
  }
1503
1519
  } else if (this.commands.length) {
1504
1520
  // This command has subcommands and nothing hooked up at this level, so display help.
1505
1521
  this.help({ error: true });
1506
1522
  } else {
1523
+ checkForUnknownOptions();
1507
1524
  // fall through for caller to handle after calling .parse()
1508
1525
  }
1509
1526
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "commander",
3
- "version": "7.0.0",
3
+ "version": "7.1.0",
4
4
  "description": "the complete solution for node.js command-line programs",
5
5
  "keywords": [
6
6
  "commander",
@@ -19,18 +19,21 @@
19
19
  "url": "https://github.com/tj/commander.js.git"
20
20
  },
21
21
  "scripts": {
22
- "lint": "eslint index.js \"tests/**/*.js\"",
22
+ "lint": "eslint index.js esm.mjs \"tests/**/*.js\"",
23
23
  "typescript-lint": "eslint typings/*.ts",
24
24
  "test": "jest && npm run test-typings",
25
- "test-typings": "tsc -p tsconfig.json",
25
+ "test-esm": "node --experimental-modules ./tests/esm-test.mjs",
26
+ "test-typings": "tsd",
26
27
  "typescript-checkJS": "tsc --allowJS --checkJS index.js --noEmit",
27
- "test-all": "npm run test && npm run lint && npm run typescript-lint && npm run typescript-checkJS"
28
+ "test-all": "npm run test && npm run lint && npm run typescript-lint && npm run typescript-checkJS && npm run test-esm"
28
29
  },
29
- "main": "index",
30
+ "main": "./index.js",
30
31
  "files": [
31
32
  "index.js",
33
+ "esm.mjs",
32
34
  "typings/index.d.ts"
33
35
  ],
36
+ "type": "commonjs",
34
37
  "dependencies": {},
35
38
  "devDependencies": {
36
39
  "@types/jest": "^26.0.20",
@@ -42,9 +45,10 @@
42
45
  "eslint-plugin-jest": "^24.1.3",
43
46
  "jest": "^26.6.3",
44
47
  "standard": "^16.0.3",
48
+ "tsd": "^0.14.0",
45
49
  "typescript": "^4.1.2"
46
50
  },
47
- "typings": "typings/index.d.ts",
51
+ "types": "typings/index.d.ts",
48
52
  "jest": {
49
53
  "collectCoverage": true
50
54
  },