commander 13.0.0-0 → 13.0.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/Readme.md CHANGED
@@ -921,9 +921,11 @@ program.helpCommand('assist [command]', 'show assistance');
921
921
  ### More configuration
922
922
 
923
923
  The built-in help is formatted using the Help class.
924
- You can configure the Help behaviour by modifying data properties and methods using `.configureHelp()`, or by subclassing using `.createHelp()` if you prefer.
924
+ You can configure the help by modifying data properties and methods using `.configureHelp()`, or by subclassing Help using `.createHelp()` .
925
925
 
926
- For more detail see (./docs/help-in-depth.md)
926
+ Simple properties include `sortSubcommands`, `sortOptions`, and `showGlobalOptions`. You can add color using the style methods like `styleTitle()`.
927
+
928
+ For more detail and examples of changing the displayed text, color, and layout see (./docs/help-in-depth.md)
927
929
 
928
930
  ## Custom event listeners
929
931
 
@@ -957,8 +959,6 @@ program.parse(['--port', '80'], { from: 'user' }); // just user supplied argumen
957
959
 
958
960
  Use parseAsync instead of parse if any of your action handlers are async.
959
961
 
960
- If you want to parse multiple times, create a new program each time. Calling parse does not clear out any previous state.
961
-
962
962
  ### Parsing Configuration
963
963
 
964
964
  If the default parsing does not suit your needs, there are some behaviours to support other usage patterns.
package/lib/command.js CHANGED
@@ -55,6 +55,7 @@ class Command extends EventEmitter {
55
55
  /** @type {(boolean | string)} */
56
56
  this._showHelpAfterError = false;
57
57
  this._showSuggestionAfterError = true;
58
+ this._savedState = null; // used in save/restoreStateBeforeParse
58
59
 
59
60
  // see configureOutput() for docs
60
61
  this._outputConfiguration = {
@@ -1069,6 +1070,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1069
1070
  */
1070
1071
 
1071
1072
  parse(argv, parseOptions) {
1073
+ this._prepareForParse();
1072
1074
  const userArgs = this._prepareUserArgs(argv, parseOptions);
1073
1075
  this._parseCommand([], userArgs);
1074
1076
 
@@ -1097,12 +1099,82 @@ Expecting one of '${allowedValues.join("', '")}'`);
1097
1099
  */
1098
1100
 
1099
1101
  async parseAsync(argv, parseOptions) {
1102
+ this._prepareForParse();
1100
1103
  const userArgs = this._prepareUserArgs(argv, parseOptions);
1101
1104
  await this._parseCommand([], userArgs);
1102
1105
 
1103
1106
  return this;
1104
1107
  }
1105
1108
 
1109
+ _prepareForParse() {
1110
+ if (this._savedState === null) {
1111
+ this.saveStateBeforeParse();
1112
+ } else {
1113
+ this.restoreStateBeforeParse();
1114
+ }
1115
+ }
1116
+
1117
+ /**
1118
+ * Called the first time parse is called to save state and allow a restore before subsequent calls to parse.
1119
+ * Not usually called directly, but available for subclasses to save their custom state.
1120
+ *
1121
+ * This is called in a lazy way. Only commands used in parsing chain will have state saved.
1122
+ */
1123
+ saveStateBeforeParse() {
1124
+ this._savedState = {
1125
+ // name is stable if supplied by author, but may be unspecified for root command and deduced during parsing
1126
+ _name: this._name,
1127
+ // option values before parse have default values (including false for negated options)
1128
+ // shallow clones
1129
+ _optionValues: { ...this._optionValues },
1130
+ _optionValueSources: { ...this._optionValueSources },
1131
+ };
1132
+ }
1133
+
1134
+ /**
1135
+ * Restore state before parse for calls after the first.
1136
+ * Not usually called directly, but available for subclasses to save their custom state.
1137
+ *
1138
+ * This is called in a lazy way. Only commands used in parsing chain will have state restored.
1139
+ */
1140
+ restoreStateBeforeParse() {
1141
+ if (this._storeOptionsAsProperties)
1142
+ throw new Error(`Can not call parse again when storeOptionsAsProperties is true.
1143
+ - either make a new Command for each call to parse, or stop storing options as properties`);
1144
+
1145
+ // clear state from _prepareUserArgs
1146
+ this._name = this._savedState._name;
1147
+ this._scriptPath = null;
1148
+ this.rawArgs = [];
1149
+ // clear state from setOptionValueWithSource
1150
+ this._optionValues = { ...this._savedState._optionValues };
1151
+ this._optionValueSources = { ...this._savedState._optionValueSources };
1152
+ // clear state from _parseCommand
1153
+ this.args = [];
1154
+ // clear state from _processArguments
1155
+ this.processedArgs = [];
1156
+ }
1157
+
1158
+ /**
1159
+ * Throw if expected executable is missing. Add lots of help for author.
1160
+ *
1161
+ * @param {string} executableFile
1162
+ * @param {string} executableDir
1163
+ * @param {string} subcommandName
1164
+ */
1165
+ _checkForMissingExecutable(executableFile, executableDir, subcommandName) {
1166
+ if (fs.existsSync(executableFile)) return;
1167
+
1168
+ const executableDirMessage = executableDir
1169
+ ? `searched for local subcommand relative to directory '${executableDir}'`
1170
+ : 'no directory for search for local subcommand, use .executableDir() to supply a custom directory';
1171
+ const executableMissing = `'${executableFile}' does not exist
1172
+ - if '${subcommandName}' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead
1173
+ - if the default executable name is not suitable, use the executableFile option to supply a custom name or path
1174
+ - ${executableDirMessage}`;
1175
+ throw new Error(executableMissing);
1176
+ }
1177
+
1106
1178
  /**
1107
1179
  * Execute a sub-command executable.
1108
1180
  *
@@ -1186,6 +1258,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
1186
1258
  proc = childProcess.spawn(executableFile, args, { stdio: 'inherit' });
1187
1259
  }
1188
1260
  } else {
1261
+ this._checkForMissingExecutable(
1262
+ executableFile,
1263
+ executableDir,
1264
+ subcommand._name,
1265
+ );
1189
1266
  args.unshift(executableFile);
1190
1267
  // add executable arguments to spawn
1191
1268
  args = incrementNodeInspectorPort(process.execArgv).concat(args);
@@ -1224,14 +1301,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
1224
1301
  proc.on('error', (err) => {
1225
1302
  // @ts-ignore: because err.code is an unknown property
1226
1303
  if (err.code === 'ENOENT') {
1227
- const executableDirMessage = executableDir
1228
- ? `searched for local subcommand relative to directory '${executableDir}'`
1229
- : 'no directory for search for local subcommand, use .executableDir() to supply a custom directory';
1230
- const executableMissing = `'${executableFile}' does not exist
1231
- - if '${subcommand._name}' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead
1232
- - if the default executable name is not suitable, use the executableFile option to supply a custom name or path
1233
- - ${executableDirMessage}`;
1234
- throw new Error(executableMissing);
1304
+ this._checkForMissingExecutable(
1305
+ executableFile,
1306
+ executableDir,
1307
+ subcommand._name,
1308
+ );
1235
1309
  // @ts-ignore: because err.code is an unknown property
1236
1310
  } else if (err.code === 'EACCES') {
1237
1311
  throw new Error(`'${executableFile}' not executable`);
@@ -1261,6 +1335,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1261
1335
  const subCommand = this._findCommand(commandName);
1262
1336
  if (!subCommand) this.help({ error: true });
1263
1337
 
1338
+ subCommand._prepareForParse();
1264
1339
  let promiseChain;
1265
1340
  promiseChain = this._chainOrCallSubCommandHook(
1266
1341
  promiseChain,
@@ -1638,6 +1713,8 @@ Expecting one of '${allowedValues.join("', '")}'`);
1638
1713
  * Parse options from `argv` removing known options,
1639
1714
  * and return argv split into operands and unknown arguments.
1640
1715
  *
1716
+ * Side effects: modifies command by storing options. Does not reset state if called again.
1717
+ *
1641
1718
  * Examples:
1642
1719
  *
1643
1720
  * argv => operands, unknown
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "commander",
3
- "version": "13.0.0-0",
3
+ "version": "13.0.0",
4
4
  "description": "the complete solution for node.js command-line programs",
5
5
  "keywords": [
6
6
  "commander",
@@ -63,7 +63,7 @@
63
63
  "@eslint/js": "^9.4.0",
64
64
  "@types/jest": "^29.2.4",
65
65
  "@types/node": "^22.7.4",
66
- "eslint": "^8.57.1",
66
+ "eslint": "^9.17.0",
67
67
  "eslint-config-prettier": "^9.1.0",
68
68
  "eslint-plugin-jest": "^28.3.0",
69
69
  "globals": "^15.7.0",
@@ -1,8 +1,6 @@
1
1
  // Type definitions for commander
2
2
  // Original definitions by: Alan Agius <https://github.com/alan-agius4>, Marcelo Dezem <https://github.com/mdezem>, vvakame <https://github.com/vvakame>, Jules Randolph <https://github.com/sveinburne>
3
3
 
4
- // Using method rather than property for method-signature-style, to document method overloads separately. Allow either.
5
- /* eslint-disable @typescript-eslint/method-signature-style */
6
4
  /* eslint-disable @typescript-eslint/no-explicit-any */
7
5
 
8
6
  // This is a trick to encourage editor to suggest the known literals while still
@@ -823,10 +821,28 @@ export class Command {
823
821
  parseOptions?: ParseOptions,
824
822
  ): Promise<this>;
825
823
 
824
+ /**
825
+ * Called the first time parse is called to save state and allow a restore before subsequent calls to parse.
826
+ * Not usually called directly, but available for subclasses to save their custom state.
827
+ *
828
+ * This is called in a lazy way. Only commands used in parsing chain will have state saved.
829
+ */
830
+ saveStateBeforeParse(): void;
831
+
832
+ /**
833
+ * Restore state before parse for calls after the first.
834
+ * Not usually called directly, but available for subclasses to save their custom state.
835
+ *
836
+ * This is called in a lazy way. Only commands used in parsing chain will have state restored.
837
+ */
838
+ restoreStateBeforeParse(): void;
839
+
826
840
  /**
827
841
  * Parse options from `argv` removing known options,
828
842
  * and return argv split into operands and unknown arguments.
829
843
  *
844
+ * Side effects: modifies command by storing options. Does not reset state if called again.
845
+ *
830
846
  * argv => operands, unknown
831
847
  * --known kkk op => [op], []
832
848
  * op --known kkk => [op], []