@rslib/core 0.2.2 → 0.3.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.
@@ -41,10 +41,10 @@ module.exports = require("node:process");
41
41
 
42
42
  /***/ }),
43
43
 
44
- /***/ 654:
44
+ /***/ 429:
45
45
  /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
46
46
 
47
- const { InvalidArgumentError } = __nccwpck_require__(829);
47
+ const { InvalidArgumentError } = __nccwpck_require__(848);
48
48
 
49
49
  class Argument {
50
50
  /**
@@ -197,7 +197,7 @@ exports.humanReadableArgName = humanReadableArgName;
197
197
 
198
198
  /***/ }),
199
199
 
200
- /***/ 955:
200
+ /***/ 745:
201
201
  /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
202
202
 
203
203
  const EventEmitter = (__nccwpck_require__(673).EventEmitter);
@@ -206,11 +206,11 @@ const path = __nccwpck_require__(411);
206
206
  const fs = __nccwpck_require__(561);
207
207
  const process = __nccwpck_require__(742);
208
208
 
209
- const { Argument, humanReadableArgName } = __nccwpck_require__(654);
210
- const { CommanderError } = __nccwpck_require__(829);
211
- const { Help } = __nccwpck_require__(567);
212
- const { Option, DualOptions } = __nccwpck_require__(230);
213
- const { suggestSimilar } = __nccwpck_require__(241);
209
+ const { Argument, humanReadableArgName } = __nccwpck_require__(429);
210
+ const { CommanderError } = __nccwpck_require__(848);
211
+ const { Help, stripColor } = __nccwpck_require__(613);
212
+ const { Option, DualOptions } = __nccwpck_require__(234);
213
+ const { suggestSimilar } = __nccwpck_require__(824);
214
214
 
215
215
  class Command extends EventEmitter {
216
216
  /**
@@ -227,7 +227,7 @@ class Command extends EventEmitter {
227
227
  this.options = [];
228
228
  this.parent = null;
229
229
  this._allowUnknownOption = false;
230
- this._allowExcessArguments = true;
230
+ this._allowExcessArguments = false;
231
231
  /** @type {Argument[]} */
232
232
  this.registeredArguments = [];
233
233
  this._args = this.registeredArguments; // deprecated old name
@@ -257,16 +257,22 @@ class Command extends EventEmitter {
257
257
  /** @type {(boolean | string)} */
258
258
  this._showHelpAfterError = false;
259
259
  this._showSuggestionAfterError = true;
260
+ this._savedState = null; // used in save/restoreStateBeforeParse
260
261
 
261
- // see .configureOutput() for docs
262
+ // see configureOutput() for docs
262
263
  this._outputConfiguration = {
263
264
  writeOut: (str) => process.stdout.write(str),
264
265
  writeErr: (str) => process.stderr.write(str),
266
+ outputError: (str, write) => write(str),
265
267
  getOutHelpWidth: () =>
266
268
  process.stdout.isTTY ? process.stdout.columns : undefined,
267
269
  getErrHelpWidth: () =>
268
270
  process.stderr.isTTY ? process.stderr.columns : undefined,
269
- outputError: (str, write) => write(str),
271
+ getOutHasColors: () =>
272
+ useColor() ?? (process.stdout.isTTY && process.stdout.hasColors?.()),
273
+ getErrHasColors: () =>
274
+ useColor() ?? (process.stderr.isTTY && process.stderr.hasColors?.()),
275
+ stripColor: (str) => stripColor(str),
270
276
  };
271
277
 
272
278
  this._hidden = false;
@@ -415,14 +421,18 @@ class Command extends EventEmitter {
415
421
  *
416
422
  * The configuration properties are all functions:
417
423
  *
418
- * // functions to change where being written, stdout and stderr
424
+ * // change how output being written, defaults to stdout and stderr
419
425
  * writeOut(str)
420
426
  * writeErr(str)
421
- * // matching functions to specify width for wrapping help
427
+ * // change how output being written for errors, defaults to writeErr
428
+ * outputError(str, write) // used for displaying errors and not used for displaying help
429
+ * // specify width for wrapping help
422
430
  * getOutHelpWidth()
423
431
  * getErrHelpWidth()
424
- * // functions based on what is being written out
425
- * outputError(str, write) // used for displaying errors, and not used for displaying help
432
+ * // color support, currently only used with Help
433
+ * getOutHasColors()
434
+ * getErrHasColors()
435
+ * stripColor() // used to remove ANSI escape codes if output does not have colors
426
436
  *
427
437
  * @param {object} [configuration] - configuration options
428
438
  * @return {(Command | object)} `this` command for chaining, or stored configuration
@@ -1262,6 +1272,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1262
1272
  */
1263
1273
 
1264
1274
  parse(argv, parseOptions) {
1275
+ this._prepareForParse();
1265
1276
  const userArgs = this._prepareUserArgs(argv, parseOptions);
1266
1277
  this._parseCommand([], userArgs);
1267
1278
 
@@ -1290,12 +1301,82 @@ Expecting one of '${allowedValues.join("', '")}'`);
1290
1301
  */
1291
1302
 
1292
1303
  async parseAsync(argv, parseOptions) {
1304
+ this._prepareForParse();
1293
1305
  const userArgs = this._prepareUserArgs(argv, parseOptions);
1294
1306
  await this._parseCommand([], userArgs);
1295
1307
 
1296
1308
  return this;
1297
1309
  }
1298
1310
 
1311
+ _prepareForParse() {
1312
+ if (this._savedState === null) {
1313
+ this.saveStateBeforeParse();
1314
+ } else {
1315
+ this.restoreStateBeforeParse();
1316
+ }
1317
+ }
1318
+
1319
+ /**
1320
+ * Called the first time parse is called to save state and allow a restore before subsequent calls to parse.
1321
+ * Not usually called directly, but available for subclasses to save their custom state.
1322
+ *
1323
+ * This is called in a lazy way. Only commands used in parsing chain will have state saved.
1324
+ */
1325
+ saveStateBeforeParse() {
1326
+ this._savedState = {
1327
+ // name is stable if supplied by author, but may be unspecified for root command and deduced during parsing
1328
+ _name: this._name,
1329
+ // option values before parse have default values (including false for negated options)
1330
+ // shallow clones
1331
+ _optionValues: { ...this._optionValues },
1332
+ _optionValueSources: { ...this._optionValueSources },
1333
+ };
1334
+ }
1335
+
1336
+ /**
1337
+ * Restore state before parse for calls after the first.
1338
+ * Not usually called directly, but available for subclasses to save their custom state.
1339
+ *
1340
+ * This is called in a lazy way. Only commands used in parsing chain will have state restored.
1341
+ */
1342
+ restoreStateBeforeParse() {
1343
+ if (this._storeOptionsAsProperties)
1344
+ throw new Error(`Can not call parse again when storeOptionsAsProperties is true.
1345
+ - either make a new Command for each call to parse, or stop storing options as properties`);
1346
+
1347
+ // clear state from _prepareUserArgs
1348
+ this._name = this._savedState._name;
1349
+ this._scriptPath = null;
1350
+ this.rawArgs = [];
1351
+ // clear state from setOptionValueWithSource
1352
+ this._optionValues = { ...this._savedState._optionValues };
1353
+ this._optionValueSources = { ...this._savedState._optionValueSources };
1354
+ // clear state from _parseCommand
1355
+ this.args = [];
1356
+ // clear state from _processArguments
1357
+ this.processedArgs = [];
1358
+ }
1359
+
1360
+ /**
1361
+ * Throw if expected executable is missing. Add lots of help for author.
1362
+ *
1363
+ * @param {string} executableFile
1364
+ * @param {string} executableDir
1365
+ * @param {string} subcommandName
1366
+ */
1367
+ _checkForMissingExecutable(executableFile, executableDir, subcommandName) {
1368
+ if (fs.existsSync(executableFile)) return;
1369
+
1370
+ const executableDirMessage = executableDir
1371
+ ? `searched for local subcommand relative to directory '${executableDir}'`
1372
+ : 'no directory for search for local subcommand, use .executableDir() to supply a custom directory';
1373
+ const executableMissing = `'${executableFile}' does not exist
1374
+ - if '${subcommandName}' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead
1375
+ - if the default executable name is not suitable, use the executableFile option to supply a custom name or path
1376
+ - ${executableDirMessage}`;
1377
+ throw new Error(executableMissing);
1378
+ }
1379
+
1299
1380
  /**
1300
1381
  * Execute a sub-command executable.
1301
1382
  *
@@ -1336,7 +1417,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1336
1417
  let resolvedScriptPath; // resolve possible symlink for installed npm binary
1337
1418
  try {
1338
1419
  resolvedScriptPath = fs.realpathSync(this._scriptPath);
1339
- } catch (err) {
1420
+ } catch {
1340
1421
  resolvedScriptPath = this._scriptPath;
1341
1422
  }
1342
1423
  executableDir = path.resolve(
@@ -1379,6 +1460,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
1379
1460
  proc = childProcess.spawn(executableFile, args, { stdio: 'inherit' });
1380
1461
  }
1381
1462
  } else {
1463
+ this._checkForMissingExecutable(
1464
+ executableFile,
1465
+ executableDir,
1466
+ subcommand._name,
1467
+ );
1382
1468
  args.unshift(executableFile);
1383
1469
  // add executable arguments to spawn
1384
1470
  args = incrementNodeInspectorPort(process.execArgv).concat(args);
@@ -1417,14 +1503,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
1417
1503
  proc.on('error', (err) => {
1418
1504
  // @ts-ignore: because err.code is an unknown property
1419
1505
  if (err.code === 'ENOENT') {
1420
- const executableDirMessage = executableDir
1421
- ? `searched for local subcommand relative to directory '${executableDir}'`
1422
- : 'no directory for search for local subcommand, use .executableDir() to supply a custom directory';
1423
- const executableMissing = `'${executableFile}' does not exist
1424
- - if '${subcommand._name}' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead
1425
- - if the default executable name is not suitable, use the executableFile option to supply a custom name or path
1426
- - ${executableDirMessage}`;
1427
- throw new Error(executableMissing);
1506
+ this._checkForMissingExecutable(
1507
+ executableFile,
1508
+ executableDir,
1509
+ subcommand._name,
1510
+ );
1428
1511
  // @ts-ignore: because err.code is an unknown property
1429
1512
  } else if (err.code === 'EACCES') {
1430
1513
  throw new Error(`'${executableFile}' not executable`);
@@ -1454,6 +1537,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1454
1537
  const subCommand = this._findCommand(commandName);
1455
1538
  if (!subCommand) this.help({ error: true });
1456
1539
 
1540
+ subCommand._prepareForParse();
1457
1541
  let promiseChain;
1458
1542
  promiseChain = this._chainOrCallSubCommandHook(
1459
1543
  promiseChain,
@@ -1831,6 +1915,8 @@ Expecting one of '${allowedValues.join("', '")}'`);
1831
1915
  * Parse options from `argv` removing known options,
1832
1916
  * and return argv split into operands and unknown arguments.
1833
1917
  *
1918
+ * Side effects: modifies command by storing options. Does not reset state if called again.
1919
+ *
1834
1920
  * Examples:
1835
1921
  *
1836
1922
  * argv => operands, unknown
@@ -2456,31 +2542,49 @@ Expecting one of '${allowedValues.join("', '")}'`);
2456
2542
 
2457
2543
  helpInformation(contextOptions) {
2458
2544
  const helper = this.createHelp();
2459
- if (helper.helpWidth === undefined) {
2460
- helper.helpWidth =
2461
- contextOptions && contextOptions.error
2462
- ? this._outputConfiguration.getErrHelpWidth()
2463
- : this._outputConfiguration.getOutHelpWidth();
2464
- }
2465
- return helper.formatHelp(this, helper);
2545
+ const context = this._getOutputContext(contextOptions);
2546
+ helper.prepareContext({
2547
+ error: context.error,
2548
+ helpWidth: context.helpWidth,
2549
+ outputHasColors: context.hasColors,
2550
+ });
2551
+ const text = helper.formatHelp(this, helper);
2552
+ if (context.hasColors) return text;
2553
+ return this._outputConfiguration.stripColor(text);
2466
2554
  }
2467
2555
 
2468
2556
  /**
2557
+ * @typedef HelpContext
2558
+ * @type {object}
2559
+ * @property {boolean} error
2560
+ * @property {number} helpWidth
2561
+ * @property {boolean} hasColors
2562
+ * @property {function} write - includes stripColor if needed
2563
+ *
2564
+ * @returns {HelpContext}
2469
2565
  * @private
2470
2566
  */
2471
2567
 
2472
- _getHelpContext(contextOptions) {
2568
+ _getOutputContext(contextOptions) {
2473
2569
  contextOptions = contextOptions || {};
2474
- const context = { error: !!contextOptions.error };
2475
- let write;
2476
- if (context.error) {
2477
- write = (arg) => this._outputConfiguration.writeErr(arg);
2570
+ const error = !!contextOptions.error;
2571
+ let baseWrite;
2572
+ let hasColors;
2573
+ let helpWidth;
2574
+ if (error) {
2575
+ baseWrite = (str) => this._outputConfiguration.writeErr(str);
2576
+ hasColors = this._outputConfiguration.getErrHasColors();
2577
+ helpWidth = this._outputConfiguration.getErrHelpWidth();
2478
2578
  } else {
2479
- write = (arg) => this._outputConfiguration.writeOut(arg);
2579
+ baseWrite = (str) => this._outputConfiguration.writeOut(str);
2580
+ hasColors = this._outputConfiguration.getOutHasColors();
2581
+ helpWidth = this._outputConfiguration.getOutHelpWidth();
2480
2582
  }
2481
- context.write = contextOptions.write || write;
2482
- context.command = this;
2483
- return context;
2583
+ const write = (str) => {
2584
+ if (!hasColors) str = this._outputConfiguration.stripColor(str);
2585
+ return baseWrite(str);
2586
+ };
2587
+ return { error, write, hasColors, helpWidth };
2484
2588
  }
2485
2589
 
2486
2590
  /**
@@ -2497,14 +2601,21 @@ Expecting one of '${allowedValues.join("', '")}'`);
2497
2601
  deprecatedCallback = contextOptions;
2498
2602
  contextOptions = undefined;
2499
2603
  }
2500
- const context = this._getHelpContext(contextOptions);
2604
+
2605
+ const outputContext = this._getOutputContext(contextOptions);
2606
+ /** @type {HelpTextEventContext} */
2607
+ const eventContext = {
2608
+ error: outputContext.error,
2609
+ write: outputContext.write,
2610
+ command: this,
2611
+ };
2501
2612
 
2502
2613
  this._getCommandAndAncestors()
2503
2614
  .reverse()
2504
- .forEach((command) => command.emit('beforeAllHelp', context));
2505
- this.emit('beforeHelp', context);
2615
+ .forEach((command) => command.emit('beforeAllHelp', eventContext));
2616
+ this.emit('beforeHelp', eventContext);
2506
2617
 
2507
- let helpInformation = this.helpInformation(context);
2618
+ let helpInformation = this.helpInformation({ error: outputContext.error });
2508
2619
  if (deprecatedCallback) {
2509
2620
  helpInformation = deprecatedCallback(helpInformation);
2510
2621
  if (
@@ -2514,14 +2625,14 @@ Expecting one of '${allowedValues.join("', '")}'`);
2514
2625
  throw new Error('outputHelp callback must return a string or a Buffer');
2515
2626
  }
2516
2627
  }
2517
- context.write(helpInformation);
2628
+ outputContext.write(helpInformation);
2518
2629
 
2519
2630
  if (this._getHelpOption()?.long) {
2520
2631
  this.emit(this._getHelpOption().long); // deprecated
2521
2632
  }
2522
- this.emit('afterHelp', context);
2633
+ this.emit('afterHelp', eventContext);
2523
2634
  this._getCommandAndAncestors().forEach((command) =>
2524
- command.emit('afterAllHelp', context),
2635
+ command.emit('afterAllHelp', eventContext),
2525
2636
  );
2526
2637
  }
2527
2638
 
@@ -2541,6 +2652,8 @@ Expecting one of '${allowedValues.join("', '")}'`);
2541
2652
  helpOption(flags, description) {
2542
2653
  // Support disabling built-in help option.
2543
2654
  if (typeof flags === 'boolean') {
2655
+ // true is not an expected value. Do something sensible but no unit-test.
2656
+ // istanbul ignore if
2544
2657
  if (flags) {
2545
2658
  this._helpOption = this._helpOption ?? undefined; // preserve existing option
2546
2659
  } else {
@@ -2594,7 +2707,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2594
2707
 
2595
2708
  help(contextOptions) {
2596
2709
  this.outputHelp(contextOptions);
2597
- let exitCode = process.exitCode || 0;
2710
+ let exitCode = Number(process.exitCode ?? 0); // process.exitCode does allow a string or an integer, but we prefer just a number
2598
2711
  if (
2599
2712
  exitCode === 0 &&
2600
2713
  contextOptions &&
@@ -2607,6 +2720,15 @@ Expecting one of '${allowedValues.join("', '")}'`);
2607
2720
  this._exit(exitCode, 'commander.help', '(outputHelp)');
2608
2721
  }
2609
2722
 
2723
+ /**
2724
+ * // Do a little typing to coordinate emit and listener for the help text events.
2725
+ * @typedef HelpTextEventContext
2726
+ * @type {object}
2727
+ * @property {boolean} error
2728
+ * @property {Command} command
2729
+ * @property {function} write
2730
+ */
2731
+
2610
2732
  /**
2611
2733
  * Add additional text to be displayed with the built-in help.
2612
2734
  *
@@ -2617,14 +2739,16 @@ Expecting one of '${allowedValues.join("', '")}'`);
2617
2739
  * @param {(string | Function)} text - string to add, or a function returning a string
2618
2740
  * @return {Command} `this` command for chaining
2619
2741
  */
2742
+
2620
2743
  addHelpText(position, text) {
2621
2744
  const allowedValues = ['beforeAll', 'before', 'after', 'afterAll'];
2622
2745
  if (!allowedValues.includes(position)) {
2623
2746
  throw new Error(`Unexpected value for position to addHelpText.
2624
2747
  Expecting one of '${allowedValues.join("', '")}'`);
2625
2748
  }
2749
+
2626
2750
  const helpEvent = `${position}Help`;
2627
- this.on(helpEvent, (context) => {
2751
+ this.on(helpEvent, (/** @type {HelpTextEventContext} */ context) => {
2628
2752
  let helpStr;
2629
2753
  if (typeof text === 'function') {
2630
2754
  helpStr = text({ error: context.error, command: context.command });
@@ -2708,12 +2832,41 @@ function incrementNodeInspectorPort(args) {
2708
2832
  });
2709
2833
  }
2710
2834
 
2835
+ /**
2836
+ * @returns {boolean | undefined}
2837
+ * @package
2838
+ */
2839
+ function useColor() {
2840
+ // Test for common conventions.
2841
+ // NB: the observed behaviour is in combination with how author adds color! For example:
2842
+ // - we do not test NODE_DISABLE_COLORS, but util:styletext does
2843
+ // - we do test NO_COLOR, but Chalk does not
2844
+ //
2845
+ // References:
2846
+ // https://no-color.org
2847
+ // https://bixense.com/clicolors/
2848
+ // https://github.com/nodejs/node/blob/0a00217a5f67ef4a22384cfc80eb6dd9a917fdc1/lib/internal/tty.js#L109
2849
+ // https://github.com/chalk/supports-color/blob/c214314a14bcb174b12b3014b2b0a8de375029ae/index.js#L33
2850
+ // (https://force-color.org recent web page from 2023, does not match major javascript implementations)
2851
+
2852
+ if (
2853
+ process.env.NO_COLOR ||
2854
+ process.env.FORCE_COLOR === '0' ||
2855
+ process.env.FORCE_COLOR === 'false'
2856
+ )
2857
+ return false;
2858
+ if (process.env.FORCE_COLOR || process.env.CLICOLOR_FORCE !== undefined)
2859
+ return true;
2860
+ return undefined;
2861
+ }
2862
+
2711
2863
  exports.Command = Command;
2864
+ exports.useColor = useColor; // exporting for tests
2712
2865
 
2713
2866
 
2714
2867
  /***/ }),
2715
2868
 
2716
- /***/ 829:
2869
+ /***/ 848:
2717
2870
  /***/ ((__unused_webpack_module, exports) => {
2718
2871
 
2719
2872
  /**
@@ -2759,10 +2912,10 @@ exports.InvalidArgumentError = InvalidArgumentError;
2759
2912
 
2760
2913
  /***/ }),
2761
2914
 
2762
- /***/ 567:
2915
+ /***/ 613:
2763
2916
  /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
2764
2917
 
2765
- const { humanReadableArgName } = __nccwpck_require__(654);
2918
+ const { humanReadableArgName } = __nccwpck_require__(429);
2766
2919
 
2767
2920
  /**
2768
2921
  * TypeScript import types for JSDoc, used by Visual Studio Code IntelliSense and `npm run typescript-checkJS`
@@ -2776,11 +2929,24 @@ const { humanReadableArgName } = __nccwpck_require__(654);
2776
2929
  class Help {
2777
2930
  constructor() {
2778
2931
  this.helpWidth = undefined;
2932
+ this.minWidthToWrap = 40;
2779
2933
  this.sortSubcommands = false;
2780
2934
  this.sortOptions = false;
2781
2935
  this.showGlobalOptions = false;
2782
2936
  }
2783
2937
 
2938
+ /**
2939
+ * prepareContext is called by Commander after applying overrides from `Command.configureHelp()`
2940
+ * and just before calling `formatHelp()`.
2941
+ *
2942
+ * Commander just uses the helpWidth and the rest is provided for optional use by more complex subclasses.
2943
+ *
2944
+ * @param {{ error?: boolean, helpWidth?: number, outputHasColors?: boolean }} contextOptions
2945
+ */
2946
+ prepareContext(contextOptions) {
2947
+ this.helpWidth = this.helpWidth ?? contextOptions.helpWidth ?? 80;
2948
+ }
2949
+
2784
2950
  /**
2785
2951
  * Get an array of the visible subcommands. Includes a placeholder for the implicit help command, if there is one.
2786
2952
  *
@@ -2955,7 +3121,12 @@ class Help {
2955
3121
 
2956
3122
  longestSubcommandTermLength(cmd, helper) {
2957
3123
  return helper.visibleCommands(cmd).reduce((max, command) => {
2958
- return Math.max(max, helper.subcommandTerm(command).length);
3124
+ return Math.max(
3125
+ max,
3126
+ this.displayWidth(
3127
+ helper.styleSubcommandTerm(helper.subcommandTerm(command)),
3128
+ ),
3129
+ );
2959
3130
  }, 0);
2960
3131
  }
2961
3132
 
@@ -2969,7 +3140,10 @@ class Help {
2969
3140
 
2970
3141
  longestOptionTermLength(cmd, helper) {
2971
3142
  return helper.visibleOptions(cmd).reduce((max, option) => {
2972
- return Math.max(max, helper.optionTerm(option).length);
3143
+ return Math.max(
3144
+ max,
3145
+ this.displayWidth(helper.styleOptionTerm(helper.optionTerm(option))),
3146
+ );
2973
3147
  }, 0);
2974
3148
  }
2975
3149
 
@@ -2983,7 +3157,10 @@ class Help {
2983
3157
 
2984
3158
  longestGlobalOptionTermLength(cmd, helper) {
2985
3159
  return helper.visibleGlobalOptions(cmd).reduce((max, option) => {
2986
- return Math.max(max, helper.optionTerm(option).length);
3160
+ return Math.max(
3161
+ max,
3162
+ this.displayWidth(helper.styleOptionTerm(helper.optionTerm(option))),
3163
+ );
2987
3164
  }, 0);
2988
3165
  }
2989
3166
 
@@ -2997,7 +3174,12 @@ class Help {
2997
3174
 
2998
3175
  longestArgumentTermLength(cmd, helper) {
2999
3176
  return helper.visibleArguments(cmd).reduce((max, argument) => {
3000
- return Math.max(max, helper.argumentTerm(argument).length);
3177
+ return Math.max(
3178
+ max,
3179
+ this.displayWidth(
3180
+ helper.styleArgumentTerm(helper.argumentTerm(argument)),
3181
+ ),
3182
+ );
3001
3183
  }, 0);
3002
3184
  }
3003
3185
 
@@ -3114,11 +3296,11 @@ class Help {
3114
3296
  );
3115
3297
  }
3116
3298
  if (extraInfo.length > 0) {
3117
- const extraDescripton = `(${extraInfo.join(', ')})`;
3299
+ const extraDescription = `(${extraInfo.join(', ')})`;
3118
3300
  if (argument.description) {
3119
- return `${argument.description} ${extraDescripton}`;
3301
+ return `${argument.description} ${extraDescription}`;
3120
3302
  }
3121
- return extraDescripton;
3303
+ return extraDescription;
3122
3304
  }
3123
3305
  return argument.description;
3124
3306
  }
@@ -3133,71 +3315,73 @@ class Help {
3133
3315
 
3134
3316
  formatHelp(cmd, helper) {
3135
3317
  const termWidth = helper.padWidth(cmd, helper);
3136
- const helpWidth = helper.helpWidth || 80;
3137
- const itemIndentWidth = 2;
3138
- const itemSeparatorWidth = 2; // between term and description
3139
- function formatItem(term, description) {
3140
- if (description) {
3141
- const fullText = `${term.padEnd(termWidth + itemSeparatorWidth)}${description}`;
3142
- return helper.wrap(
3143
- fullText,
3144
- helpWidth - itemIndentWidth,
3145
- termWidth + itemSeparatorWidth,
3146
- );
3147
- }
3148
- return term;
3149
- }
3150
- function formatList(textArray) {
3151
- return textArray.join('\n').replace(/^/gm, ' '.repeat(itemIndentWidth));
3318
+ const helpWidth = helper.helpWidth ?? 80; // in case prepareContext() was not called
3319
+
3320
+ function callFormatItem(term, description) {
3321
+ return helper.formatItem(term, termWidth, description, helper);
3152
3322
  }
3153
3323
 
3154
3324
  // Usage
3155
- let output = [`Usage: ${helper.commandUsage(cmd)}`, ''];
3325
+ let output = [
3326
+ `${helper.styleTitle('Usage:')} ${helper.styleUsage(helper.commandUsage(cmd))}`,
3327
+ '',
3328
+ ];
3156
3329
 
3157
3330
  // Description
3158
3331
  const commandDescription = helper.commandDescription(cmd);
3159
3332
  if (commandDescription.length > 0) {
3160
3333
  output = output.concat([
3161
- helper.wrap(commandDescription, helpWidth, 0),
3334
+ helper.boxWrap(
3335
+ helper.styleCommandDescription(commandDescription),
3336
+ helpWidth,
3337
+ ),
3162
3338
  '',
3163
3339
  ]);
3164
3340
  }
3165
3341
 
3166
3342
  // Arguments
3167
3343
  const argumentList = helper.visibleArguments(cmd).map((argument) => {
3168
- return formatItem(
3169
- helper.argumentTerm(argument),
3170
- helper.argumentDescription(argument),
3344
+ return callFormatItem(
3345
+ helper.styleArgumentTerm(helper.argumentTerm(argument)),
3346
+ helper.styleArgumentDescription(helper.argumentDescription(argument)),
3171
3347
  );
3172
3348
  });
3173
3349
  if (argumentList.length > 0) {
3174
- output = output.concat(['Arguments:', formatList(argumentList), '']);
3350
+ output = output.concat([
3351
+ helper.styleTitle('Arguments:'),
3352
+ ...argumentList,
3353
+ '',
3354
+ ]);
3175
3355
  }
3176
3356
 
3177
3357
  // Options
3178
3358
  const optionList = helper.visibleOptions(cmd).map((option) => {
3179
- return formatItem(
3180
- helper.optionTerm(option),
3181
- helper.optionDescription(option),
3359
+ return callFormatItem(
3360
+ helper.styleOptionTerm(helper.optionTerm(option)),
3361
+ helper.styleOptionDescription(helper.optionDescription(option)),
3182
3362
  );
3183
3363
  });
3184
3364
  if (optionList.length > 0) {
3185
- output = output.concat(['Options:', formatList(optionList), '']);
3365
+ output = output.concat([
3366
+ helper.styleTitle('Options:'),
3367
+ ...optionList,
3368
+ '',
3369
+ ]);
3186
3370
  }
3187
3371
 
3188
- if (this.showGlobalOptions) {
3372
+ if (helper.showGlobalOptions) {
3189
3373
  const globalOptionList = helper
3190
3374
  .visibleGlobalOptions(cmd)
3191
3375
  .map((option) => {
3192
- return formatItem(
3193
- helper.optionTerm(option),
3194
- helper.optionDescription(option),
3376
+ return callFormatItem(
3377
+ helper.styleOptionTerm(helper.optionTerm(option)),
3378
+ helper.styleOptionDescription(helper.optionDescription(option)),
3195
3379
  );
3196
3380
  });
3197
3381
  if (globalOptionList.length > 0) {
3198
3382
  output = output.concat([
3199
- 'Global Options:',
3200
- formatList(globalOptionList),
3383
+ helper.styleTitle('Global Options:'),
3384
+ ...globalOptionList,
3201
3385
  '',
3202
3386
  ]);
3203
3387
  }
@@ -3205,18 +3389,103 @@ class Help {
3205
3389
 
3206
3390
  // Commands
3207
3391
  const commandList = helper.visibleCommands(cmd).map((cmd) => {
3208
- return formatItem(
3209
- helper.subcommandTerm(cmd),
3210
- helper.subcommandDescription(cmd),
3392
+ return callFormatItem(
3393
+ helper.styleSubcommandTerm(helper.subcommandTerm(cmd)),
3394
+ helper.styleSubcommandDescription(helper.subcommandDescription(cmd)),
3211
3395
  );
3212
3396
  });
3213
3397
  if (commandList.length > 0) {
3214
- output = output.concat(['Commands:', formatList(commandList), '']);
3398
+ output = output.concat([
3399
+ helper.styleTitle('Commands:'),
3400
+ ...commandList,
3401
+ '',
3402
+ ]);
3215
3403
  }
3216
3404
 
3217
3405
  return output.join('\n');
3218
3406
  }
3219
3407
 
3408
+ /**
3409
+ * Return display width of string, ignoring ANSI escape sequences. Used in padding and wrapping calculations.
3410
+ *
3411
+ * @param {string} str
3412
+ * @returns {number}
3413
+ */
3414
+ displayWidth(str) {
3415
+ return stripColor(str).length;
3416
+ }
3417
+
3418
+ /**
3419
+ * Style the title for displaying in the help. Called with 'Usage:', 'Options:', etc.
3420
+ *
3421
+ * @param {string} str
3422
+ * @returns {string}
3423
+ */
3424
+ styleTitle(str) {
3425
+ return str;
3426
+ }
3427
+
3428
+ styleUsage(str) {
3429
+ // Usage has lots of parts the user might like to color separately! Assume default usage string which is formed like:
3430
+ // command subcommand [options] [command] <foo> [bar]
3431
+ return str
3432
+ .split(' ')
3433
+ .map((word) => {
3434
+ if (word === '[options]') return this.styleOptionText(word);
3435
+ if (word === '[command]') return this.styleSubcommandText(word);
3436
+ if (word[0] === '[' || word[0] === '<')
3437
+ return this.styleArgumentText(word);
3438
+ return this.styleCommandText(word); // Restrict to initial words?
3439
+ })
3440
+ .join(' ');
3441
+ }
3442
+ styleCommandDescription(str) {
3443
+ return this.styleDescriptionText(str);
3444
+ }
3445
+ styleOptionDescription(str) {
3446
+ return this.styleDescriptionText(str);
3447
+ }
3448
+ styleSubcommandDescription(str) {
3449
+ return this.styleDescriptionText(str);
3450
+ }
3451
+ styleArgumentDescription(str) {
3452
+ return this.styleDescriptionText(str);
3453
+ }
3454
+ styleDescriptionText(str) {
3455
+ return str;
3456
+ }
3457
+ styleOptionTerm(str) {
3458
+ return this.styleOptionText(str);
3459
+ }
3460
+ styleSubcommandTerm(str) {
3461
+ // This is very like usage with lots of parts! Assume default string which is formed like:
3462
+ // subcommand [options] <foo> [bar]
3463
+ return str
3464
+ .split(' ')
3465
+ .map((word) => {
3466
+ if (word === '[options]') return this.styleOptionText(word);
3467
+ if (word[0] === '[' || word[0] === '<')
3468
+ return this.styleArgumentText(word);
3469
+ return this.styleSubcommandText(word); // Restrict to initial words?
3470
+ })
3471
+ .join(' ');
3472
+ }
3473
+ styleArgumentTerm(str) {
3474
+ return this.styleArgumentText(str);
3475
+ }
3476
+ styleOptionText(str) {
3477
+ return str;
3478
+ }
3479
+ styleArgumentText(str) {
3480
+ return str;
3481
+ }
3482
+ styleSubcommandText(str) {
3483
+ return str;
3484
+ }
3485
+ styleCommandText(str) {
3486
+ return str;
3487
+ }
3488
+
3220
3489
  /**
3221
3490
  * Calculate the pad width from the maximum term length.
3222
3491
  *
@@ -3235,61 +3504,134 @@ class Help {
3235
3504
  }
3236
3505
 
3237
3506
  /**
3238
- * Wrap the given string to width characters per line, with lines after the first indented.
3239
- * Do not wrap if insufficient room for wrapping (minColumnWidth), or string is manually formatted.
3507
+ * Detect manually wrapped and indented strings by checking for line break followed by whitespace.
3240
3508
  *
3241
3509
  * @param {string} str
3242
- * @param {number} width
3243
- * @param {number} indent
3244
- * @param {number} [minColumnWidth=40]
3245
- * @return {string}
3510
+ * @returns {boolean}
3511
+ */
3512
+ preformatted(str) {
3513
+ return /\n[^\S\r\n]/.test(str);
3514
+ }
3515
+
3516
+ /**
3517
+ * Format the "item", which consists of a term and description. Pad the term and wrap the description, indenting the following lines.
3518
+ *
3519
+ * So "TTT", 5, "DDD DDDD DD DDD" might be formatted for this.helpWidth=17 like so:
3520
+ * TTT DDD DDDD
3521
+ * DD DDD
3246
3522
  *
3523
+ * @param {string} term
3524
+ * @param {number} termWidth
3525
+ * @param {string} description
3526
+ * @param {Help} helper
3527
+ * @returns {string}
3247
3528
  */
3529
+ formatItem(term, termWidth, description, helper) {
3530
+ const itemIndent = 2;
3531
+ const itemIndentStr = ' '.repeat(itemIndent);
3532
+ if (!description) return itemIndentStr + term;
3248
3533
 
3249
- wrap(str, width, indent, minColumnWidth = 40) {
3250
- // Full \s characters, minus the linefeeds.
3251
- const indents =
3252
- ' \\f\\t\\v\u00a0\u1680\u2000-\u200a\u202f\u205f\u3000\ufeff';
3253
- // Detect manually wrapped and indented strings by searching for line break followed by spaces.
3254
- const manualIndent = new RegExp(`[\\n][${indents}]+`);
3255
- if (str.match(manualIndent)) return str;
3256
- // Do not wrap if not enough room for a wrapped column of text (as could end up with a word per line).
3257
- const columnWidth = width - indent;
3258
- if (columnWidth < minColumnWidth) return str;
3259
-
3260
- const leadingStr = str.slice(0, indent);
3261
- const columnText = str.slice(indent).replace('\r\n', '\n');
3262
- const indentString = ' '.repeat(indent);
3263
- const zeroWidthSpace = '\u200B';
3264
- const breaks = `\\s${zeroWidthSpace}`;
3265
- // Match line end (so empty lines don't collapse),
3266
- // or as much text as will fit in column, or excess text up to first break.
3267
- const regex = new RegExp(
3268
- `\n|.{1,${columnWidth - 1}}([${breaks}]|$)|[^${breaks}]+?([${breaks}]|$)`,
3269
- 'g',
3534
+ // Pad the term out to a consistent width, so descriptions are aligned.
3535
+ const paddedTerm = term.padEnd(
3536
+ termWidth + term.length - helper.displayWidth(term),
3270
3537
  );
3271
- const lines = columnText.match(regex) || [];
3538
+
3539
+ // Format the description.
3540
+ const spacerWidth = 2; // between term and description
3541
+ const helpWidth = this.helpWidth ?? 80; // in case prepareContext() was not called
3542
+ const remainingWidth = helpWidth - termWidth - spacerWidth - itemIndent;
3543
+ let formattedDescription;
3544
+ if (
3545
+ remainingWidth < this.minWidthToWrap ||
3546
+ helper.preformatted(description)
3547
+ ) {
3548
+ formattedDescription = description;
3549
+ } else {
3550
+ const wrappedDescription = helper.boxWrap(description, remainingWidth);
3551
+ formattedDescription = wrappedDescription.replace(
3552
+ /\n/g,
3553
+ '\n' + ' '.repeat(termWidth + spacerWidth),
3554
+ );
3555
+ }
3556
+
3557
+ // Construct and overall indent.
3272
3558
  return (
3273
- leadingStr +
3274
- lines
3275
- .map((line, i) => {
3276
- if (line === '\n') return ''; // preserve empty lines
3277
- return (i > 0 ? indentString : '') + line.trimEnd();
3278
- })
3279
- .join('\n')
3559
+ itemIndentStr +
3560
+ paddedTerm +
3561
+ ' '.repeat(spacerWidth) +
3562
+ formattedDescription.replace(/\n/g, `\n${itemIndentStr}`)
3280
3563
  );
3281
3564
  }
3565
+
3566
+ /**
3567
+ * Wrap a string at whitespace, preserving existing line breaks.
3568
+ * Wrapping is skipped if the width is less than `minWidthToWrap`.
3569
+ *
3570
+ * @param {string} str
3571
+ * @param {number} width
3572
+ * @returns {string}
3573
+ */
3574
+ boxWrap(str, width) {
3575
+ if (width < this.minWidthToWrap) return str;
3576
+
3577
+ const rawLines = str.split(/\r\n|\n/);
3578
+ // split up text by whitespace
3579
+ const chunkPattern = /[\s]*[^\s]+/g;
3580
+ const wrappedLines = [];
3581
+ rawLines.forEach((line) => {
3582
+ const chunks = line.match(chunkPattern);
3583
+ if (chunks === null) {
3584
+ wrappedLines.push('');
3585
+ return;
3586
+ }
3587
+
3588
+ let sumChunks = [chunks.shift()];
3589
+ let sumWidth = this.displayWidth(sumChunks[0]);
3590
+ chunks.forEach((chunk) => {
3591
+ const visibleWidth = this.displayWidth(chunk);
3592
+ // Accumulate chunks while they fit into width.
3593
+ if (sumWidth + visibleWidth <= width) {
3594
+ sumChunks.push(chunk);
3595
+ sumWidth += visibleWidth;
3596
+ return;
3597
+ }
3598
+ wrappedLines.push(sumChunks.join(''));
3599
+
3600
+ const nextChunk = chunk.trimStart(); // trim space at line break
3601
+ sumChunks = [nextChunk];
3602
+ sumWidth = this.displayWidth(nextChunk);
3603
+ });
3604
+ wrappedLines.push(sumChunks.join(''));
3605
+ });
3606
+
3607
+ return wrappedLines.join('\n');
3608
+ }
3609
+ }
3610
+
3611
+ /**
3612
+ * Strip style ANSI escape sequences from the string. In particular, SGR (Select Graphic Rendition) codes.
3613
+ *
3614
+ * @param {string} str
3615
+ * @returns {string}
3616
+ * @package
3617
+ */
3618
+
3619
+ function stripColor(str) {
3620
+ // eslint-disable-next-line no-control-regex
3621
+ const sgrPattern = /\x1b\[\d*(;\d*)*m/g;
3622
+ return str.replace(sgrPattern, '');
3282
3623
  }
3283
3624
 
3284
3625
  exports.Help = Help;
3626
+ exports.stripColor = stripColor;
3285
3627
 
3286
3628
 
3287
3629
  /***/ }),
3288
3630
 
3289
- /***/ 230:
3631
+ /***/ 234:
3290
3632
  /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
3291
3633
 
3292
- const { InvalidArgumentError } = __nccwpck_require__(829);
3634
+ const { InvalidArgumentError } = __nccwpck_require__(848);
3293
3635
 
3294
3636
  class Option {
3295
3637
  /**
@@ -3498,13 +3840,16 @@ class Option {
3498
3840
 
3499
3841
  /**
3500
3842
  * Return option name, in a camelcase format that can be used
3501
- * as a object attribute key.
3843
+ * as an object attribute key.
3502
3844
  *
3503
3845
  * @return {string}
3504
3846
  */
3505
3847
 
3506
3848
  attributeName() {
3507
- return camelcase(this.name().replace(/^no-/, ''));
3849
+ if (this.negate) {
3850
+ return camelcase(this.name().replace(/^no-/, ''));
3851
+ }
3852
+ return camelcase(this.name());
3508
3853
  }
3509
3854
 
3510
3855
  /**
@@ -3603,17 +3948,32 @@ function camelcase(str) {
3603
3948
  function splitOptionFlags(flags) {
3604
3949
  let shortFlag;
3605
3950
  let longFlag;
3606
- // Use original very loose parsing to maintain backwards compatibility for now,
3607
- // which allowed for example unintended `-sw, --short-word` [sic].
3608
- const flagParts = flags.split(/[ |,]+/);
3609
- if (flagParts.length > 1 && !/^[[<]/.test(flagParts[1]))
3610
- shortFlag = flagParts.shift();
3611
- longFlag = flagParts.shift();
3612
- // Add support for lone short flag without significantly changing parsing!
3613
- if (!shortFlag && /^-[^-]$/.test(longFlag)) {
3614
- shortFlag = longFlag;
3615
- longFlag = undefined;
3616
- }
3951
+ // short flag, single dash and single character
3952
+ const shortFlagExp = /^-[^-]$/;
3953
+ // long flag, double dash and at least one character
3954
+ const longFlagExp = /^--[^-]/;
3955
+
3956
+ const flagParts = flags.split(/[ |,]+/).concat('guard');
3957
+ if (shortFlagExp.test(flagParts[0])) shortFlag = flagParts.shift();
3958
+ if (longFlagExp.test(flagParts[0])) longFlag = flagParts.shift();
3959
+
3960
+ // Check for some unsupported flags that people try.
3961
+ if (/^-[^-][^-]/.test(flagParts[0]))
3962
+ throw new Error(
3963
+ `invalid Option flags, short option is dash and single character: '${flags}'`,
3964
+ );
3965
+ if (shortFlag && shortFlagExp.test(flagParts[0]))
3966
+ throw new Error(
3967
+ `invalid Option flags, more than one short flag: '${flags}'`,
3968
+ );
3969
+ if (longFlag && longFlagExp.test(flagParts[0]))
3970
+ throw new Error(
3971
+ `invalid Option flags, more than one long flag: '${flags}'`,
3972
+ );
3973
+ // Generic error if failed to find a flag or an unexpected flag left over.
3974
+ if (!(shortFlag || longFlag) || flagParts[0].startsWith('-'))
3975
+ throw new Error(`invalid Option flags: '${flags}'`);
3976
+
3617
3977
  return { shortFlag, longFlag };
3618
3978
  }
3619
3979
 
@@ -3623,7 +3983,7 @@ exports.DualOptions = DualOptions;
3623
3983
 
3624
3984
  /***/ }),
3625
3985
 
3626
- /***/ 241:
3986
+ /***/ 824:
3627
3987
  /***/ ((__unused_webpack_module, exports) => {
3628
3988
 
3629
3989
  const maxDistance = 3;
@@ -3773,11 +4133,11 @@ var __webpack_exports__ = {};
3773
4133
  // This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
3774
4134
  (() => {
3775
4135
  var exports = __webpack_exports__;
3776
- const { Argument } = __nccwpck_require__(654);
3777
- const { Command } = __nccwpck_require__(955);
3778
- const { CommanderError, InvalidArgumentError } = __nccwpck_require__(829);
3779
- const { Help } = __nccwpck_require__(567);
3780
- const { Option } = __nccwpck_require__(230);
4136
+ const { Argument } = __nccwpck_require__(429);
4137
+ const { Command } = __nccwpck_require__(745);
4138
+ const { CommanderError, InvalidArgumentError } = __nccwpck_require__(848);
4139
+ const { Help } = __nccwpck_require__(613);
4140
+ const { Option } = __nccwpck_require__(234);
3781
4141
 
3782
4142
  exports.program = new Command();
3783
4143