Setting the file. One moment.
Command · Wix Replatform · wix/skills · Skills Docs
ContentsBack to the top of the page vendor/commander/lib/command.js
vendor/commander/lib/ command.js
JavaScript · 2,790 lines · 86 KB
{ Argument, humanReadableArgName }
from
'./argument.js'
;
9 import { CommanderError } from './error.js' ;
10 import { Help } from './help.js' ;
11 import { Option, DualOptions } from './option.js' ;
12 import { suggestSimilar } from './suggestSimilar.js' ;
13
14 export class Command extends EventEmitter {
15 /**
16 * Initialize a new `Command`.
17 *
18 * @param {string} [name]
19 */
20
21 constructor ( name ) {
22 super ();
23 /** @type {Command[]} */
24 this .commands = [];
25 /** @type {Option[]} */
26 this .options = [];
27 this .parent = null ;
28 this ._allowUnknownOption = false ;
29 this ._allowExcessArguments = false ;
30 /** @type {Argument[]} */
31 this .registeredArguments = [];
32 this ._args = this .registeredArguments; // deprecated old name
33 /** @type {string[]} */
34 this .args = []; // cli args with options removed
35 this .rawArgs = [];
36 this .processedArgs = []; // like .args but after custom processing and collecting variadic
37 this ._scriptPath = null ;
38 this ._name = name || '' ;
39 this ._optionValues = {};
40 this ._optionValueSources = {}; // default, env, cli etc
41 this ._storeOptionsAsProperties = false ;
42 this ._actionHandler = null ;
43 this ._executableHandler = false ;
44 this ._executableFile = null ; // custom name for executable
45 this ._executableDir = null ; // custom search directory for subcommands
46 this ._defaultCommandName = null ;
47 this ._exitCallback = null ;
48 this ._aliases = [];
49 this ._combineFlagAndOptionalValue = true ;
50 this ._description = '' ;
51 this ._summary = '' ;
52 this ._argsDescription = undefined ; // legacy
53 this ._enablePositionalOptions = false ;
54 this ._passThroughOptions = false ;
55 this ._lifeCycleHooks = {}; // a hash of arrays
56 /** @type {(boolean | string)} */
57 this ._showHelpAfterError = false ;
58 this ._showSuggestionAfterError = true ;
59 this ._savedState = null ; // used in save/restoreStateBeforeParse
60
61 // see configureOutput() for docs
62 this ._outputConfiguration = {
63 writeOut : ( str ) => process.stdout. write (str),
64 writeErr : ( str ) => process.stderr. write (str),
65 outputError : ( str , write ) => write (str),
66 getOutHelpWidth : () =>
67 process.stdout.isTTY ? process.stdout.columns : undefined ,
68 getErrHelpWidth : () =>
69 process.stderr.isTTY ? process.stderr.columns : undefined ,
70 getOutHasColors : () =>
71 useColor () ?? (process.stdout.isTTY && process.stdout. hasColors ?.()),
72 getErrHasColors : () =>
73 useColor () ?? (process.stderr.isTTY && process.stderr. hasColors ?.()),
74 stripColor : ( str ) => stripVTControlCharacters (str),
75 };
76
77 this ._hidden = false ;
78 /** @type {(Option | null | undefined)} */
79 this ._helpOption = undefined ; // Lazy created on demand. May be null if help option is disabled.
80 this ._addImplicitHelpCommand = undefined ; // undecided whether true or false yet, not inherited
81 /** @type {Command} */
82 this ._helpCommand = undefined ; // lazy initialised, inherited
83 this ._helpConfiguration = {};
84 /** @type {string | undefined} */
85 this ._helpGroupHeading = undefined ; // soft initialised when added to parent
86 /** @type {string | undefined} */
87 this ._defaultCommandGroup = undefined ;
88 /** @type {string | undefined} */
89 this ._defaultOptionGroup = undefined ;
90 }
91
92 /**
93 * Copy settings that are useful to have in common across root command and subcommands.
94 *
95 * (Used internally when adding a command using `.command()` so subcommands inherit parent settings.)
96 *
97 * @param {Command} sourceCommand
98 * @return {Command} `this` command for chaining
99 */
100 copyInheritedSettings ( sourceCommand ) {
101 this ._outputConfiguration = sourceCommand._outputConfiguration;
102 this ._helpOption = sourceCommand._helpOption;
103 this ._helpCommand = sourceCommand._helpCommand;
104 this ._helpConfiguration = sourceCommand._helpConfiguration;
105 this ._exitCallback = sourceCommand._exitCallback;
106 this ._storeOptionsAsProperties = sourceCommand._storeOptionsAsProperties;
107 this ._combineFlagAndOptionalValue =
108 sourceCommand._combineFlagAndOptionalValue;
109 this ._allowExcessArguments = sourceCommand._allowExcessArguments;
110 this ._enablePositionalOptions = sourceCommand._enablePositionalOptions;
111 this ._showHelpAfterError = sourceCommand._showHelpAfterError;
112 this ._showSuggestionAfterError = sourceCommand._showSuggestionAfterError;
113
114 return this ;
115 }
116
117 /**
118 * @returns {Command[]}
119 * @private
120 */
121
122 _getCommandAndAncestors () {
123 const result = [];
124 // eslint-disable-next-line @typescript-eslint/no-this-alias
125 for ( let command = this ; command; command = command.parent) {
126 result. push (command);
127 }
128 return result;
129 }
130
131 /**
132 * Define a command.
133 *
134 * There are two styles of command: pay attention to where to put the description.
135 *
136 * @example
137 * // Command implemented using action handler (description is supplied separately to `.command`)
138 * program
139 * .command('clone <source> [destination]')
140 * .description('clone a repository into a newly created directory')
141 * .action((source, destination) => {
142 * console.log('clone command called');
143 * });
144 *
145 * // Command implemented using separate executable file (description is second parameter to `.command`)
146 * program
147 * .command('start <service>', 'start named service')
148 * .command('stop [service]', 'stop named service, or all if no name supplied');
149 *
150 * @param {string} nameAndArgs - command name and arguments, args are `<required>` or `[optional]` and last may also be `variadic...`
151 * @param {(object | string)} [actionOptsOrExecDesc] - configuration options (for action), or description (for executable)
152 * @param {object} [execOpts] - configuration options (for executable)
153 * @return {Command} returns new command for action handler, or `this` for executable command
154 */
155
156 command ( nameAndArgs , actionOptsOrExecDesc , execOpts ) {
157 let desc = actionOptsOrExecDesc;
158 let opts = execOpts;
159 if ( typeof desc === 'object' && desc !== null ) {
160 opts = desc;
161 desc = null ;
162 }
163 opts = opts || {};
164 const [, name , args ] = nameAndArgs. match ( /( [ ^ ] + ) * ( . * )/ );
165
166 const cmd = this . createCommand (name);
167 if (desc) {
168 cmd. description (desc);
169 cmd._executableHandler = true ;
170 }
171 if (opts.isDefault) this ._defaultCommandName = cmd._name;
172 cmd._hidden = !! (opts.noHelp || opts.hidden); // noHelp is deprecated old name for hidden
173 cmd._executableFile = opts.executableFile || null ; // Custom name for executable file, set missing to null to match constructor
174 if (args) cmd. arguments (args);
175 this . _registerCommand (cmd);
176 cmd.parent = this ;
177 cmd. copyInheritedSettings ( this );
178
179 if (desc) return this ;
180 return cmd;
181 }
182
183 /**
184 * Factory routine to create a new unattached command.
185 *
186 * See .command() for creating an attached subcommand, which uses this routine to
187 * create the command. You can override createCommand to customise subcommands.
188 *
189 * @param {string} [name]
190 * @return {Command} new command
191 */
192
193 createCommand ( name ) {
194 return new Command (name);
195 }
196
197 /**
198 * You can customise the help with a subclass of Help by overriding createHelp,
199 * or by overriding Help properties using configureHelp().
200 *
201 * @return {Help}
202 */
203
204 createHelp () {
205 return Object. assign ( new Help (), this . configureHelp ());
206 }
207
208 /**
209 * You can customise the help by overriding Help properties using configureHelp(),
210 * or with a subclass of Help by overriding createHelp().
211 *
212 * @param {object} [configuration] - configuration options
213 * @return {(Command | object)} `this` command for chaining, or stored configuration
214 */
215
216 configureHelp ( configuration ) {
217 if (configuration === undefined ) return this ._helpConfiguration;
218
219 this ._helpConfiguration = configuration;
220 return this ;
221 }
222
223 /**
224 * The default output goes to stdout and stderr. You can customise this for special
225 * applications. You can also customise the display of errors by overriding outputError.
226 *
227 * The configuration properties are all functions:
228 *
229 * // change how output being written, defaults to stdout and stderr
230 * writeOut(str)
231 * writeErr(str)
232 * // change how output being written for errors, defaults to writeErr
233 * outputError(str, write) // used for displaying errors and not used for displaying help
234 * // specify width for wrapping help
235 * getOutHelpWidth()
236 * getErrHelpWidth()
237 * // color support, currently only used with Help
238 * getOutHasColors()
239 * getErrHasColors()
240 * stripColor() // used to remove ANSI escape codes if output does not have colors
241 *
242 * @param {object} [configuration] - configuration options
243 * @return {(Command | object)} `this` command for chaining, or stored configuration
244 */
245
246 configureOutput ( configuration ) {
247 if (configuration === undefined ) return this ._outputConfiguration;
248
249 this ._outputConfiguration = {
250 ... this ._outputConfiguration,
251 ... configuration,
252 };
253 return this ;
254 }
255
256 /**
257 * Display the help or a custom message after an error occurs.
258 *
259 * @param {(boolean|string)} [displayHelp]
260 * @return {Command} `this` command for chaining
261 */
262 showHelpAfterError ( displayHelp = true ) {
263 if ( typeof displayHelp !== 'string' ) displayHelp = !! displayHelp;
264 this ._showHelpAfterError = displayHelp;
265 return this ;
266 }
267
268 /**
269 * Display suggestion of similar commands for unknown commands, or options for unknown options.
270 *
271 * @param {boolean} [displaySuggestion]
272 * @return {Command} `this` command for chaining
273 */
274 showSuggestionAfterError ( displaySuggestion = true ) {
275 this ._showSuggestionAfterError = !! displaySuggestion;
276 return this ;
277 }
278
279 /**
280 * Add a prepared subcommand.
281 *
282 * See .command() for creating an attached subcommand which inherits settings from its parent.
283 *
284 * @param {Command} cmd - new subcommand
285 * @param {object} [opts] - configuration options
286 * @return {Command} `this` command for chaining
287 */
288
289 addCommand ( cmd , opts ) {
290 if ( ! cmd._name) {
291 throw new Error ( `Command passed to .addCommand() must have a name
292 - specify the name in Command constructor or using .name()` );
293 }
294
295 opts = opts || {};
296 if (opts.isDefault) this ._defaultCommandName = cmd._name;
297 if (opts.noHelp || opts.hidden) cmd._hidden = true ; // modifying passed command due to existing implementation
298
299 this . _registerCommand (cmd);
300 cmd.parent = this ;
301 cmd. _checkForBrokenPassThrough ();
302
303 return this ;
304 }
305
306 /**
307 * Factory routine to create a new unattached argument.
308 *
309 * See .argument() for creating an attached argument, which uses this routine to
310 * create the argument. You can override createArgument to return a custom argument.
311 *
312 * @param {string} name
313 * @param {string} [description]
314 * @return {Argument} new argument
315 */
316
317 createArgument ( name , description ) {
318 return new Argument (name, description);
319 }
320
321 /**
322 * Define argument syntax for command.
323 *
324 * The default is that the argument is required, and you can explicitly
325 * indicate this with <> around the name. Put [] around the name for an optional argument.
326 *
327 * @example
328 * program.argument('<input-file>');
329 * program.argument('[output-file]');
330 *
331 * @param {string} name
332 * @param {string} [description]
333 * @param {(Function|*)} [parseArg] - custom argument processing function or default value
334 * @param {*} [defaultValue]
335 * @return {Command} `this` command for chaining
336 */
337 argument ( name , description , parseArg , defaultValue ) {
338 const argument = this . createArgument (name, description);
339 if ( typeof parseArg === 'function' ) {
340 argument. default (defaultValue). argParser (parseArg);
341 } else {
342 argument. default (parseArg);
343 }
344 this . addArgument (argument);
345 return this ;
346 }
347
348 /**
349 * Define argument syntax for command, adding multiple at once (without descriptions).
350 *
351 * See also .argument().
352 *
353 * @example
354 * program.arguments('<cmd> [env]');
355 *
356 * @param {string} names
357 * @return {Command} `this` command for chaining
358 */
359
360 arguments ( names ) {
361 names
362 . trim ()
363 . split ( / + / )
364 . forEach (( detail ) => {
365 this . argument (detail);
366 });
367 return this ;
368 }
369
370 /**
371 * Define argument syntax for command, adding a prepared argument.
372 *
373 * @param {Argument} argument
374 * @return {Command} `this` command for chaining
375 */
376 addArgument ( argument ) {
377 const previousArgument = this .registeredArguments. slice ( - 1 )[ 0 ];
378 if (previousArgument?.variadic) {
379 throw new Error (
380 `only the last argument can be variadic '${ previousArgument . name () }'` ,
381 );
382 }
383 if (
384 argument.required &&
385 argument.defaultValue !== undefined &&
386 argument.parseArg === undefined
387 ) {
388 throw new Error (
389 `a default value for a required argument is never used: '${ argument . name () }'` ,
390 );
391 }
392 this .registeredArguments. push (argument);
393 return this ;
394 }
395
396 /**
397 * Customise or override default help command. By default a help command is automatically added if your command has subcommands.
398 *
399 * @example
400 * program.helpCommand('help [cmd]');
401 * program.helpCommand('help [cmd]', 'show help');
402 * program.helpCommand(false); // suppress default help command
403 * program.helpCommand(true); // add help command even if no subcommands
404 *
405 * @param {string|boolean} enableOrNameAndArgs - enable with custom name and/or arguments, or boolean to override whether added
406 * @param {string} [description] - custom description
407 * @return {Command} `this` command for chaining
408 */
409
410 helpCommand ( enableOrNameAndArgs , description ) {
411 if ( typeof enableOrNameAndArgs === 'boolean' ) {
412 this ._addImplicitHelpCommand = enableOrNameAndArgs;
413 if (enableOrNameAndArgs && this ._defaultCommandGroup) {
414 // make the command to store the group
415 this . _initCommandGroup ( this . _getHelpCommand ());
416 }
417 return this ;
418 }
419
420 const nameAndArgs = enableOrNameAndArgs ?? 'help [command]' ;
421 const [, helpName , helpArgs ] = nameAndArgs. match ( /( [ ^ ] + ) * ( . * )/ );
422 const helpDescription = description ?? 'display help for command' ;
423
424 const helpCommand = this . createCommand (helpName);
425 helpCommand. helpOption ( false );
426 if (helpArgs) helpCommand. arguments (helpArgs);
427 if (helpDescription) helpCommand. description (helpDescription);
428
429 this ._addImplicitHelpCommand = true ;
430 this ._helpCommand = helpCommand;
431 // init group unless lazy create
432 if (enableOrNameAndArgs || description) this . _initCommandGroup (helpCommand);
433
434 return this ;
435 }
436
437 /**
438 * Add prepared custom help command.
439 *
440 * @param {(Command|string|boolean)} helpCommand - custom help command, or deprecated enableOrNameAndArgs as for `.helpCommand()`
441 * @param {string} [deprecatedDescription] - deprecated custom description used with custom name only
442 * @return {Command} `this` command for chaining
443 */
444 addHelpCommand ( helpCommand , deprecatedDescription ) {
445 // If not passed an object, call through to helpCommand for backwards compatibility,
446 // as addHelpCommand was originally used like helpCommand is now.
447 if ( typeof helpCommand !== 'object' ) {
448 this . helpCommand (helpCommand, deprecatedDescription);
449 return this ;
450 }
451
452 this ._addImplicitHelpCommand = true ;
453 this ._helpCommand = helpCommand;
454 this . _initCommandGroup (helpCommand);
455 return this ;
456 }
457
458 /**
459 * Lazy create help command.
460 *
461 * @return {(Command|null)}
462 * @package
463 */
464 _getHelpCommand () {
465 const hasImplicitHelpCommand =
466 this ._addImplicitHelpCommand ??
467 ( this .commands. length &&
468 ! this ._actionHandler &&
469 ! this . _findCommand ( 'help' ));
470
471 if (hasImplicitHelpCommand) {
472 if ( this ._helpCommand === undefined ) {
473 this . helpCommand ( undefined , undefined ); // use default name and description
474 }
475 return this ._helpCommand;
476 }
477 return null ;
478 }
479
480 /**
481 * Add hook for life cycle event.
482 *
483 * @param {string} event
484 * @param {Function} listener
485 * @return {Command} `this` command for chaining
486 */
487
488 hook ( event , listener ) {
489 const allowedValues = [ 'preSubcommand' , 'preAction' , 'postAction' ];
490 if ( ! allowedValues. includes (event)) {
491 throw new Error ( `Unexpected value for event passed to hook : '${ event }'.
492 Expecting one of '${ allowedValues . join ( "', '" ) }'` );
493 }
494 if ( this ._lifeCycleHooks[event]) {
495 this ._lifeCycleHooks[event]. push (listener);
496 } else {
497 this ._lifeCycleHooks[event] = [listener];
498 }
499 return this ;
500 }
501
502 /**
503 * Register callback to use as replacement for calling process.exit.
504 *
505 * @param {Function} [fn] optional callback which will be passed a CommanderError, defaults to throwing
506 * @return {Command} `this` command for chaining
507 */
508
509 exitOverride ( fn ) {
510 if (fn) {
511 this ._exitCallback = fn;
512 } else {
513 this . _exitCallback = ( err ) => {
514 if (err.code !== 'commander.executeSubCommandAsync' ) {
515 throw err;
516 } else {
517 // Async callback from spawn events, not useful to throw.
518 }
519 };
520 }
521 return this ;
522 }
523
524 /**
525 * Call process.exit, and _exitCallback if defined.
526 *
527 * @param {number} exitCode exit code for using with process.exit
528 * @param {string} code an id string representing the error
529 * @param {string} message human-readable description of the error
530 * @return never
531 * @private
532 */
533
534 _exit ( exitCode , code , message ) {
535 if ( this ._exitCallback) {
536 this . _exitCallback ( new CommanderError (exitCode, code, message));
537 // Expecting this line is not reached.
538 }
539 process. exit (exitCode);
540 }
541
542 /**
543 * Register callback `fn` for the command.
544 *
545 * @example
546 * program
547 * .command('serve')
548 * .description('start service')
549 * .action(function() {
550 * // do work here
551 * });
552 *
553 * @param {Function} fn
554 * @return {Command} `this` command for chaining
555 */
556
557 action ( fn ) {
558 const listener = ( args ) => {
559 // The .action callback takes an extra parameter which is the command or options.
560 const expectedArgsCount = this .registeredArguments. length ;
561 const actionArgs = args. slice ( 0 , expectedArgsCount);
562 if ( this ._storeOptionsAsProperties) {
563 actionArgs[expectedArgsCount] = this ; // backwards compatible "options"
564 } else {
565 actionArgs[expectedArgsCount] = this . opts ();
566 }
567 actionArgs. push ( this );
568
569 return fn. apply ( this , actionArgs);
570 };
571 this ._actionHandler = listener;
572 return this ;
573 }
574
575 /**
576 * Factory routine to create a new unattached option.
577 *
578 * See .option() for creating an attached option, which uses this routine to
579 * create the option. You can override createOption to return a custom option.
580 *
581 * @param {string} flags
582 * @param {string} [description]
583 * @return {Option} new option
584 */
585
586 createOption ( flags , description ) {
587 return new Option (flags, description);
588 }
589
590 /**
591 * Wrap parseArgs to catch 'commander.invalidArgument'.
592 *
593 * @param {(Option | Argument)} target
594 * @param {string} value
595 * @param {*} previous
596 * @param {string} invalidArgumentMessage
597 * @private
598 */
599
600 _callParseArg ( target , value , previous , invalidArgumentMessage ) {
601 try {
602 return target. parseArg (value, previous);
603 } catch (err) {
604 if (err.code === 'commander.invalidArgument' ) {
605 const message = `${ invalidArgumentMessage } ${ err . message }` ;
606 this . error (message, { exitCode: err.exitCode, code: err.code });
607 }
608 throw err;
609 }
610 }
611
612 /**
613 * Check for option flag conflicts.
614 * Register option if no conflicts found, or throw on conflict.
615 *
616 * @param {Option} option
617 * @private
618 */
619
620 _registerOption ( option ) {
621 const matchingOption =
622 (option.short && this . _findOption (option.short)) ||
623 (option.long && this . _findOption (option.long));
624 if (matchingOption) {
625 const matchingFlag =
626 option.long && this . _findOption (option.long)
627 ? option.long
628 : option.short;
629 throw new Error ( `Cannot add option '${ option . flags }'${ this . _name && ` to command '${ this . _name }'`} due to conflicting flag '${ matchingFlag }'
630 - already used by option '${ matchingOption . flags }'` );
631 }
632
633 this . _initOptionGroup (option);
634 this .options. push (option);
635 }
636
637 /**
638 * Check for command name and alias conflicts with existing commands.
639 * Register command if no conflicts found, or throw on conflict.
640 *
641 * @param {Command} command
642 * @private
643 */
644
645 _registerCommand ( command ) {
646 const knownBy = ( cmd ) => {
647 return [cmd. name ()]. concat (cmd. aliases ());
648 };
649
650 const alreadyUsed = knownBy (command). find (( name ) =>
651 this . _findCommand (name),
652 );
653 if (alreadyUsed) {
654 const existingCmd = knownBy ( this . _findCommand (alreadyUsed)). join ( '|' );
655 const newCmd = knownBy (command). join ( '|' );
656 throw new Error (
657 `cannot add command '${ newCmd }' as already have command '${ existingCmd }'` ,
658 );
659 }
660
661 this . _initCommandGroup (command);
662 this .commands. push (command);
663 }
664
665 /**
666 * Add an option.
667 *
668 * @param {Option} option
669 * @return {Command} `this` command for chaining
670 */
671 addOption ( option ) {
672 this . _registerOption (option);
673
674 const oname = option. name ();
675 const name = option. attributeName ();
676
677 // store default value
678 if (option.defaultValue !== undefined ) {
679 this . setOptionValueWithSource (name, option.defaultValue, 'default' );
680 }
681
682 // handler for cli and env supplied values
683 const handleOptionValue = ( val , invalidValueMessage , valueSource ) => {
684 // val is null for optional option used without an optional-argument.
685 // val is undefined for boolean and negated option.
686 if (val == null && option.presetArg !== undefined ) {
687 val = option.presetArg;
688 }
689
690 // custom processing
691 const oldValue = this . getOptionValue (name);
692 if (val !== null && option.parseArg) {
693 val = this . _callParseArg (option, val, oldValue, invalidValueMessage);
694 } else if (val !== null && option.variadic) {
695 val = option. _collectValue (val, oldValue);
696 }
697
698 // Fill-in appropriate missing values. Long winded but easy to follow.
699 if (val == null ) {
700 if (option.negate) {
701 val = false ;
702 } else if (option. isBoolean () || option.optional) {
703 val = true ;
704 } else {
705 val = '' ; // not normal, parseArg might have failed or be a mock function for testing
706 }
707 }
708 this . setOptionValueWithSource (name, val, valueSource);
709 };
710
711 this . on ( 'option:' + oname, ( val ) => {
712 const invalidValueMessage = `error: option '${ option . flags }' argument '${ val }' is invalid.` ;
713 handleOptionValue (val, invalidValueMessage, 'cli' );
714 });
715
716 if (option.envVar) {
717 this . on ( 'optionEnv:' + oname, ( val ) => {
718 const invalidValueMessage = `error: option '${ option . flags }' value '${ val }' from env '${ option . envVar }' is invalid.` ;
719 handleOptionValue (val, invalidValueMessage, 'env' );
720 });
721 }
722
723 return this ;
724 }
725
726 /**
727 * Internal implementation shared by .option() and .requiredOption()
728 *
729 * @return {Command} `this` command for chaining
730 * @private
731 */
732 _optionEx ( config , flags , description , fn , defaultValue ) {
733 if ( typeof flags === 'object' && flags instanceof Option ) {
734 throw new Error (
735 'To add an Option object use addOption() instead of option() or requiredOption()' ,
736 );
737 }
738 const option = this . createOption (flags, description);
739 option. makeOptionMandatory ( !! config.mandatory);
740 if ( typeof fn === 'function' ) {
741 option. default (defaultValue). argParser (fn);
742 } else if (fn instanceof RegExp ) {
743 // deprecated
744 const regex = fn;
745 fn = ( val , def ) => {
746 const m = regex. exec (val);
747 return m ? m[ 0 ] : def;
748 };
749 option. default (defaultValue). argParser (fn);
750 } else {
751 option. default (fn);
752 }
753
754 return this . addOption (option);
755 }
756
757 /**
758 * Define option with `flags`, `description`, and optional argument parsing function or `defaultValue` or both.
759 *
760 * The `flags` string contains the short and/or long flags, separated by comma, a pipe or space. A required
761 * option-argument is indicated by `<>` and an optional option-argument by `[]`.
762 *
763 * See the README for more details, and see also addOption() and requiredOption().
764 *
765 * @example
766 * program
767 * .option('-p, --pepper', 'add pepper')
768 * .option('--pt, --pizza-type <TYPE>', 'type of pizza') // required option-argument
769 * .option('-c, --cheese [CHEESE]', 'add extra cheese', 'mozzarella') // optional option-argument with default
770 * .option('-t, --tip <VALUE>', 'add tip to purchase cost', parseFloat) // custom parse function
771 *
772 * @param {string} flags
773 * @param {string} [description]
774 * @param {(Function|*)} [parseArg] - custom option processing function or default value
775 * @param {*} [defaultValue]
776 * @return {Command} `this` command for chaining
777 */
778
779 option ( flags , description , parseArg , defaultValue ) {
780 return this . _optionEx ({}, flags, description, parseArg, defaultValue);
781 }
782
783 /**
784 * Add a required option which must have a value after parsing. This usually means
785 * the option must be specified on the command line. (Otherwise the same as .option().)
786 *
787 * The `flags` string contains the short and/or long flags, separated by comma, a pipe or space.
788 *
789 * @param {string} flags
790 * @param {string} [description]
791 * @param {(Function|*)} [parseArg] - custom option processing function or default value
792 * @param {*} [defaultValue]
793 * @return {Command} `this` command for chaining
794 */
795
796 requiredOption ( flags , description , parseArg , defaultValue ) {
797 return this . _optionEx (
798 { mandatory: true },
799 flags,
800 description,
801 parseArg,
802 defaultValue,
803 );
804 }
805
806 /**
807 * Alter parsing of short flags with optional values.
808 *
809 * @example
810 * // for `.option('-f,--flag [value]'):
811 * program.combineFlagAndOptionalValue(true); // `-f80` is treated like `--flag=80`, this is the default behaviour
812 * program.combineFlagAndOptionalValue(false) // `-fb` is treated like `-f -b`
813 *
814 * @param {boolean} [combine] - if `true` or omitted, an optional value can be specified directly after the flag.
815 * @return {Command} `this` command for chaining
816 */
817 combineFlagAndOptionalValue ( combine = true ) {
818 this ._combineFlagAndOptionalValue = !! combine;
819 return this ;
820 }
821
822 /**
823 * Allow unknown options on the command line.
824 *
825 * @param {boolean} [allowUnknown] - if `true` or omitted, no error will be thrown for unknown options.
826 * @return {Command} `this` command for chaining
827 */
828 allowUnknownOption ( allowUnknown = true ) {
829 this ._allowUnknownOption = !! allowUnknown;
830 return this ;
831 }
832
833 /**
834 * Allow excess command-arguments on the command line. Pass false to make excess arguments an error.
835 *
836 * @param {boolean} [allowExcess] - if `true` or omitted, no error will be thrown for excess arguments.
837 * @return {Command} `this` command for chaining
838 */
839 allowExcessArguments ( allowExcess = true ) {
840 this ._allowExcessArguments = !! allowExcess;
841 return this ;
842 }
843
844 /**
845 * Enable positional options. Positional means global options are specified before subcommands which lets
846 * subcommands reuse the same option names, and also enables subcommands to turn on passThroughOptions.
847 * The default behaviour is non-positional and global options may appear anywhere on the command line.
848 *
849 * @param {boolean} [positional]
850 * @return {Command} `this` command for chaining
851 */
852 enablePositionalOptions ( positional = true ) {
853 this ._enablePositionalOptions = !! positional;
854 return this ;
855 }
856
857 /**
858 * Pass through options that come after command-arguments rather than treat them as command-options,
859 * so actual command-options come before command-arguments. Turning this on for a subcommand requires
860 * positional options to have been enabled on the program (parent commands).
861 * The default behaviour is non-positional and options may appear before or after command-arguments.
862 *
863 * @param {boolean} [passThrough] for unknown options.
864 * @return {Command} `this` command for chaining
865 */
866 passThroughOptions ( passThrough = true ) {
867 this ._passThroughOptions = !! passThrough;
868 this . _checkForBrokenPassThrough ();
869 return this ;
870 }
871
872 /**
873 * @private
874 */
875
876 _checkForBrokenPassThrough () {
877 if (
878 this .parent &&
879 this ._passThroughOptions &&
880 ! this .parent._enablePositionalOptions
881 ) {
882 throw new Error (
883 `passThroughOptions cannot be used for '${ this . _name }' without turning on enablePositionalOptions for parent command(s)` ,
884 );
885 }
886 }
887
888 /**
889 * Whether to store option values as properties on command object,
890 * or store separately (specify false). In both cases the option values can be accessed using .opts().
891 *
892 * @param {boolean} [storeAsProperties = true]
893 * @return {Command} `this` command for chaining
894 */
895
896 storeOptionsAsProperties ( storeAsProperties = true ) {
897 if ( this .options. length ) {
898 throw new Error ( 'call .storeOptionsAsProperties() before adding options' );
899 }
900 if (Object. keys ( this ._optionValues). length ) {
901 throw new Error (
902 'call .storeOptionsAsProperties() before setting option values' ,
903 );
904 }
905 this ._storeOptionsAsProperties = !! storeAsProperties;
906 return this ;
907 }
908
909 /**
910 * Retrieve option value.
911 *
912 * @param {string} key
913 * @return {object} value
914 */
915
916 getOptionValue ( key ) {
917 if ( this ._storeOptionsAsProperties) {
918 return this [key];
919 }
920 return this ._optionValues[key];
921 }
922
923 /**
924 * Store option value.
925 *
926 * @param {string} key
927 * @param {object} value
928 * @return {Command} `this` command for chaining
929 */
930
931 setOptionValue ( key , value ) {
932 return this . setOptionValueWithSource (key, value, undefined );
933 }
934
935 /**
936 * Store option value and where the value came from.
937 *
938 * @param {string} key
939 * @param {object} value
940 * @param {string} source - expected values are default/config/env/cli/implied
941 * @return {Command} `this` command for chaining
942 */
943
944 setOptionValueWithSource ( key , value , source ) {
945 if ( this ._storeOptionsAsProperties) {
946 this [key] = value;
947 } else {
948 this ._optionValues[key] = value;
949 }
950 this ._optionValueSources[key] = source;
951 return this ;
952 }
953
954 /**
955 * Get source of option value.
956 * Expected values are default | config | env | cli | implied
957 *
958 * @param {string} key
959 * @return {string}
960 */
961
962 getOptionValueSource ( key ) {
963 return this ._optionValueSources[key];
964 }
965
966 /**
967 * Get source of option value. See also .optsWithGlobals().
968 * Expected values are default | config | env | cli | implied
969 *
970 * @param {string} key
971 * @return {string}
972 */
973
974 getOptionValueSourceWithGlobals ( key ) {
975 // global overwrites local, like optsWithGlobals
976 let source;
977 this . _getCommandAndAncestors (). forEach (( cmd ) => {
978 if (cmd. getOptionValueSource (key) !== undefined ) {
979 source = cmd. getOptionValueSource (key);
980 }
981 });
982 return source;
983 }
984
985 /**
986 * Get user arguments from implied or explicit arguments.
987 * Side-effects: set _scriptPath if args included script. Used for default program name, and subcommand searches.
988 *
989 * @private
990 */
991
992 _prepareUserArgs ( argv , parseOptions ) {
993 if (argv !== undefined && ! Array. isArray (argv)) {
994 throw new Error ( 'first parameter to parse must be array or undefined' );
995 }
996 parseOptions = parseOptions || {};
997
998 // auto-detect argument conventions if nothing supplied
999 if (argv === undefined && parseOptions.from === undefined ) {
1000 if (process.versions?.electron) {
1001 parseOptions.from = 'electron' ;
1002 }
1003 // check node specific options for scenarios where user CLI args follow executable without scriptname
1004 const execArgv = process.execArgv ?? [];
1005 if (
1006 execArgv. includes ( '-e' ) ||
1007 execArgv. includes ( '--eval' ) ||
1008 execArgv. includes ( '-p' ) ||
1009 execArgv. includes ( '--print' )
1010 ) {
1011 parseOptions.from = 'eval' ; // internal usage, not documented
1012 }
1013 }
1014
1015 // default to using process.argv
1016 if (argv === undefined ) {
1017 argv = process.argv;
1018 }
1019 this .rawArgs = argv. slice ();
1020
1021 // extract the user args and scriptPath
1022 let userArgs;
1023 switch (parseOptions.from) {
1024 case undefined :
1025 case 'node' :
1026 this ._scriptPath = argv[ 1 ];
1027 userArgs = argv. slice ( 2 );
1028 break ;
1029 case 'electron' :
1030 // @ts-ignore: because defaultApp is an unknown property
1031 if (process.defaultApp) {
1032 this ._scriptPath = argv[ 1 ];
1033 userArgs = argv. slice ( 2 );
1034 } else {
1035 userArgs = argv. slice ( 1 );
1036 }
1037 break ;
1038 case 'user' :
1039 userArgs = argv. slice ( 0 );
1040 break ;
1041 case 'eval' :
1042 userArgs = argv. slice ( 1 );
1043 break ;
1044 default :
1045 throw new Error (
1046 `unexpected parse option { from: '${ parseOptions . from }' }` ,
1047 );
1048 }
1049
1050 // Find default name for program from arguments.
1051 if ( ! this ._name && this ._scriptPath)
1052 this . nameFromFilename ( this ._scriptPath);
1053 this ._name = this ._name || 'program' ;
1054
1055 return userArgs;
1056 }
1057
1058 /**
1059 * Parse `argv`, setting options and invoking commands when defined.
1060 *
1061 * Use parseAsync instead of parse if any of your action handlers are async.
1062 *
1063 * Call with no parameters to parse `process.argv`. Detects Electron and special node options like `node --eval`. Easy mode!
1064 *
1065 * Or call with an array of strings to parse, and optionally where the user arguments start by specifying where the arguments are `from`:
1066 * - `'node'`: default, `argv[0]` is the application and `argv[1]` is the script being run, with user arguments after that
1067 * - `'electron'`: `argv[0]` is the application and `argv[1]` varies depending on whether the electron application is packaged
1068 * - `'user'`: just user arguments
1069 *
1070 * @example
1071 * program.parse(); // parse process.argv and auto-detect electron and special node flags
1072 * program.parse(process.argv); // assume argv[0] is app and argv[1] is script
1073 * program.parse(my-args, { from: 'user' }); // just user supplied arguments, nothing special about argv[0]
1074 *
1075 * @param {string[]} [argv] - optional, defaults to process.argv
1076 * @param {object} [parseOptions] - optionally specify style of options with from: node/user/electron
1077 * @param {string} [parseOptions.from] - where the args are from: 'node', 'user', 'electron'
1078 * @return {Command} `this` command for chaining
1079 */
1080
1081 parse ( argv , parseOptions ) {
1082 this . _prepareForParse ();
1083 const userArgs = this . _prepareUserArgs (argv, parseOptions);
1084 this . _parseCommand ([], userArgs);
1085
1086 return this ;
1087 }
1088
1089 /**
1090 * Parse `argv`, setting options and invoking commands when defined.
1091 *
1092 * Call with no parameters to parse `process.argv`. Detects Electron and special node options like `node --eval`. Easy mode!
1093 *
1094 * Or call with an array of strings to parse, and optionally where the user arguments start by specifying where the arguments are `from`:
1095 * - `'node'`: default, `argv[0]` is the application and `argv[1]` is the script being run, with user arguments after that
1096 * - `'electron'`: `argv[0]` is the application and `argv[1]` varies depending on whether the electron application is packaged
1097 * - `'user'`: just user arguments
1098 *
1099 * @example
1100 * await program.parseAsync(); // parse process.argv and auto-detect electron and special node flags
1101 * await program.parseAsync(process.argv); // assume argv[0] is app and argv[1] is script
1102 * await program.parseAsync(my-args, { from: 'user' }); // just user supplied arguments, nothing special about argv[0]
1103 *
1104 * @param {string[]} [argv]
1105 * @param {object} [parseOptions]
1106 * @param {string} parseOptions.from - where the args are from: 'node', 'user', 'electron'
1107 * @return {Promise}
1108 */
1109
1110 async parseAsync ( argv , parseOptions ) {
1111 this . _prepareForParse ();
1112 const userArgs = this . _prepareUserArgs (argv, parseOptions);
1113 await this . _parseCommand ([], userArgs);
1114
1115 return this ;
1116 }
1117
1118 _prepareForParse () {
1119 // Save the state the first time, then restore the state before each subsequent parse.
1120 if ( this ._savedState === null ) {
1121 // Do the special default of lone negated option to true, now that we have all the options.
1122 // Filter for negated options that have not been processed already.
1123 this .options
1124 . filter (
1125 ( option ) =>
1126 option.negate &&
1127 option.defaultValue === undefined &&
1128 this . getOptionValue (option. attributeName ()) === undefined ,
1129 )
1130 . forEach (( option ) => {
1131 // check for lone negated option: --no-foo without a --foo option
1132 const positiveLongFlag = option.long. replace ( / ^ --no-/ , '--' );
1133 if ( ! this . _findOption (positiveLongFlag)) {
1134 this . setOptionValueWithSource (
1135 option. attributeName (),
1136 true ,
1137 'default' ,
1138 );
1139 }
1140 });
1141
1142 this . saveStateBeforeParse ();
1143 } else {
1144 this . restoreStateBeforeParse ();
1145 }
1146 }
1147
1148 /**
1149 * Called the first time parse is called to save state and allow a restore before subsequent calls to parse.
1150 * Not usually called directly, but available for subclasses to save their custom state.
1151 *
1152 * This is called in a lazy way. Only commands used in parsing chain will have state saved.
1153 */
1154 saveStateBeforeParse () {
1155 this ._savedState = {
1156 // name is stable if supplied by author, but may be unspecified for root command and deduced during parsing
1157 _name: this ._name,
1158 // option values before parse have default values (including false for negated options)
1159 // shallow clones
1160 _optionValues: { ... this ._optionValues },
1161 _optionValueSources: { ... this ._optionValueSources },
1162 };
1163 }
1164
1165 /**
1166 * Restore state before parse for calls after the first.
1167 * Not usually called directly, but available for subclasses to save their custom state.
1168 *
1169 * This is called in a lazy way. Only commands used in parsing chain will have state restored.
1170 */
1171 restoreStateBeforeParse () {
1172 if ( this ._storeOptionsAsProperties)
1173 throw new Error ( `Can not call parse again when storeOptionsAsProperties is true.
1174 - either make a new Command for each call to parse, or stop storing options as properties` );
1175
1176 // clear state from _prepareUserArgs
1177 this ._name = this ._savedState._name;
1178 this ._scriptPath = null ;
1179 this .rawArgs = [];
1180 // clear state from setOptionValueWithSource
1181 this ._optionValues = { ... this ._savedState._optionValues };
1182 this ._optionValueSources = { ... this ._savedState._optionValueSources };
1183 // clear state from _parseCommand
1184 this .args = [];
1185 // clear state from _processArguments
1186 this .processedArgs = [];
1187 }
1188
1189 /**
1190 * Throw if expected executable is missing. Add lots of help for author.
1191 *
1192 * @param {string} executableFile
1193 * @param {string} executableDir
1194 * @param {string} subcommandName
1195 */
1196 _checkForMissingExecutable ( executableFile , executableDir , subcommandName ) {
1197 if (fs. existsSync (executableFile)) return ;
1198
1199 const executableDirMessage = executableDir
1200 ? `searched for local subcommand relative to directory '${ executableDir }'`
1201 : 'no directory for search for local subcommand, use .executableDir() to supply a custom directory' ;
1202 const executableMissing = `'${ executableFile }' does not exist
1203 - if '${ subcommandName }' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead
1204 - if the default executable name is not suitable, use the executableFile option to supply a custom name or path
1205 - ${ executableDirMessage }` ;
1206 throw new Error (executableMissing);
1207 }
1208
1209 /**
1210 * Execute a sub-command executable.
1211 *
1212 * @private
1213 */
1214
1215 _executeSubCommand ( subcommand , args ) {
1216 args = args. slice ();
1217 const sourceExt = [ '.js' , '.ts' , '.tsx' , '.mjs' , '.cjs' ];
1218
1219 function findFile ( baseDir , baseName ) {
1220 // Look for specified file
1221 const localBin = path. resolve (baseDir, baseName);
1222 if (fs. existsSync (localBin)) return localBin;
1223
1224 // Stop looking if candidate already has an expected extension.
1225 if (sourceExt. includes (path. extname (baseName))) return undefined ;
1226
1227 // Try all the extensions.
1228 const foundExt = sourceExt. find (( ext ) =>
1229 fs. existsSync ( `${ localBin }${ ext }` ),
1230 );
1231 if (foundExt) return `${ localBin }${ foundExt }` ;
1232
1233 return undefined ;
1234 }
1235
1236 // Not checking for help first. Unlikely to have mandatory and executable, and can't robustly test for help flags in external command.
1237 this . _checkForMissingMandatoryOptions ();
1238 this . _checkForConflictingOptions ();
1239
1240 // executableFile and executableDir might be full path, or just a name
1241 let executableFile =
1242 subcommand._executableFile || `${ this . _name }-${ subcommand . _name }` ;
1243 let executableDir = this ._executableDir || '' ;
1244 if ( this ._scriptPath) {
1245 let resolvedScriptPath; // resolve possible symlink for installed npm binary
1246 try {
1247 resolvedScriptPath = fs. realpathSync ( this ._scriptPath);
1248 } catch {
1249 resolvedScriptPath = this ._scriptPath;
1250 }
1251 executableDir = path. resolve (
1252 path. dirname (resolvedScriptPath),
1253 executableDir,
1254 );
1255 }
1256
1257 // Look for a local file in preference to a command in PATH.
1258 if (executableDir) {
1259 let localFile = findFile (executableDir, executableFile);
1260
1261 // Legacy search using prefix of script name instead of command name
1262 if ( ! localFile && ! subcommand._executableFile && this ._scriptPath) {
1263 const legacyName = path. basename (
1264 this ._scriptPath,
1265 path. extname ( this ._scriptPath),
1266 );
1267 if (legacyName !== this ._name) {
1268 localFile = findFile (
1269 executableDir,
1270 `${ legacyName }-${ subcommand . _name }` ,
1271 );
1272 }
1273 }
1274 executableFile = localFile || executableFile;
1275 }
1276
1277 const launchWithNode = sourceExt. includes (path. extname (executableFile));
1278
1279 let proc;
1280 if (process.platform !== 'win32' ) {
1281 if (launchWithNode) {
1282 args. unshift (executableFile);
1283 // add executable arguments to spawn
1284 args = incrementNodeInspectorPort (process.execArgv). concat (args);
1285
1286 proc = childProcess. spawn (process.argv[ 0 ], args, { stdio: 'inherit' });
1287 } else {
1288 proc = childProcess. spawn (executableFile, args, { stdio: 'inherit' });
1289 }
1290 } else {
1291 this . _checkForMissingExecutable (
1292 executableFile,
1293 executableDir,
1294 subcommand._name,
1295 );
1296 args. unshift (executableFile);
1297 // add executable arguments to spawn
1298 args = incrementNodeInspectorPort (process.execArgv). concat (args);
1299 proc = childProcess. spawn (process.execPath, args, { stdio: 'inherit' });
1300 }
1301
1302 if ( ! proc.killed) {
1303 // testing mainly to avoid leak warnings during unit tests with mocked spawn
1304 const signals = [ 'SIGUSR1' , 'SIGUSR2' , 'SIGTERM' , 'SIGINT' , 'SIGHUP' ];
1305 signals. forEach (( signal ) => {
1306 process. on (signal, () => {
1307 if (proc.killed === false && proc.exitCode === null ) {
1308 // @ts-ignore because signals not typed to known strings
1309 proc. kill (signal);
1310 }
1311 });
1312 });
1313 }
1314
1315 // By default terminate process when spawned process terminates.
1316 const exitCallback = this ._exitCallback;
1317 proc. on ( 'close' , ( code ) => {
1318 code = code ?? 1 ; // code is null if spawned process terminated due to a signal
1319 if ( ! exitCallback) {
1320 process. exit (code);
1321 } else {
1322 exitCallback (
1323 new CommanderError (
1324 code,
1325 'commander.executeSubCommandAsync' ,
1326 '(close)' ,
1327 ),
1328 );
1329 }
1330 });
1331 proc. on ( 'error' , ( err ) => {
1332 // @ts-ignore: because err.code is an unknown property
1333 if (err.code === 'ENOENT' ) {
1334 this . _checkForMissingExecutable (
1335 executableFile,
1336 executableDir,
1337 subcommand._name,
1338 );
1339 // @ts-ignore: because err.code is an unknown property
1340 } else if (err.code === 'EACCES' ) {
1341 throw new Error ( `'${ executableFile }' not executable` );
1342 }
1343 if ( ! exitCallback) {
1344 process. exit ( 1 );
1345 } else {
1346 const wrappedError = new CommanderError (
1347 1 ,
1348 'commander.executeSubCommandAsync' ,
1349 '(error)' ,
1350 );
1351 wrappedError.nestedError = err;
1352 exitCallback (wrappedError);
1353 }
1354 });
1355
1356 // Store the reference to the child process
1357 this .runningCommand = proc;
1358 }
1359
1360 /**
1361 * @private
1362 */
1363
1364 _dispatchSubcommand ( commandName , operands , unknown ) {
1365 const subCommand = this . _findCommand (commandName);
1366 if ( ! subCommand) this . help ({ error: true });
1367
1368 subCommand. _prepareForParse ();
1369 let promiseChain;
1370 promiseChain = this . _chainOrCallSubCommandHook (
1371 promiseChain,
1372 subCommand,
1373 'preSubcommand' ,
1374 );
1375 promiseChain = this . _chainOrCall (promiseChain, () => {
1376 if (subCommand._executableHandler) {
1377 this . _executeSubCommand (subCommand, operands. concat (unknown));
1378 } else {
1379 return subCommand. _parseCommand (operands, unknown);
1380 }
1381 });
1382 return promiseChain;
1383 }
1384
1385 /**
1386 * Invoke help directly if possible, or dispatch if necessary.
1387 * e.g. help foo
1388 *
1389 * @private
1390 */
1391
1392 _dispatchHelpCommand ( subcommandName ) {
1393 if ( ! subcommandName) {
1394 this . help ();
1395 }
1396 const subCommand = this . _findCommand (subcommandName);
1397 if (subCommand && ! subCommand._executableHandler) {
1398 subCommand. help ();
1399 }
1400
1401 // Fallback to parsing the help flag to invoke the help.
1402 return this . _dispatchSubcommand (
1403 subcommandName,
1404 [],
1405 [ this . _getHelpOption ()?.long ?? this . _getHelpOption ()?.short ?? '--help' ],
1406 );
1407 }
1408
1409 /**
1410 * Check this.args against expected this.registeredArguments.
1411 *
1412 * @private
1413 */
1414
1415 _checkNumberOfArguments () {
1416 // too few
1417 this .registeredArguments. forEach (( arg , i ) => {
1418 if (arg.required && this .args[i] == null ) {
1419 this . missingArgument (arg. name ());
1420 }
1421 });
1422 // too many
1423 if (
1424 this .registeredArguments. length > 0 &&
1425 this .registeredArguments[ this .registeredArguments. length - 1 ].variadic
1426 ) {
1427 return ;
1428 }
1429 if ( this .args. length > this .registeredArguments. length ) {
1430 this . _excessArguments ( this .args);
1431 }
1432 }
1433
1434 /**
1435 * Process this.args using this.registeredArguments and save as this.processedArgs!
1436 *
1437 * @private
1438 */
1439
1440 _processArguments () {
1441 const myParseArg = ( argument , value , previous ) => {
1442 // Extra processing for nice error message on parsing failure.
1443 let parsedValue = value;
1444 if (value !== null && argument.parseArg) {
1445 const invalidValueMessage = `error: command-argument value '${ value }' is invalid for argument '${ argument . name () }'.` ;
1446 parsedValue = this . _callParseArg (
1447 argument,
1448 value,
1449 previous,
1450 invalidValueMessage,
1451 );
1452 }
1453 return parsedValue;
1454 };
1455
1456 this . _checkNumberOfArguments ();
1457
1458 const processedArgs = [];
1459 this .registeredArguments. forEach (( declaredArg , index ) => {
1460 let value = declaredArg.defaultValue;
1461 if (declaredArg.variadic) {
1462 // Collect together remaining arguments for passing together as an array.
1463 if (index < this .args. length ) {
1464 value = this .args. slice (index);
1465 if (declaredArg.parseArg) {
1466 value = value. reduce (( processed , v ) => {
1467 return myParseArg (declaredArg, v, processed);
1468 }, declaredArg.defaultValue);
1469 }
1470 } else if (value === undefined ) {
1471 value = [];
1472 }
1473 } else if (index < this .args. length ) {
1474 value = this .args[index];
1475 if (declaredArg.parseArg) {
1476 value = myParseArg (declaredArg, value, declaredArg.defaultValue);
1477 }
1478 }
1479 processedArgs[index] = value;
1480 });
1481 this .processedArgs = processedArgs;
1482 }
1483
1484 /**
1485 * Once we have a promise we chain, but call synchronously until then.
1486 *
1487 * @param {(Promise|undefined)} promise
1488 * @param {Function} fn
1489 * @return {(Promise|undefined)}
1490 * @private
1491 */
1492
1493 _chainOrCall ( promise , fn ) {
1494 // thenable
1495 if (promise?.then && typeof promise.then === 'function' ) {
1496 // already have a promise, chain callback
1497 return promise. then (() => fn ());
1498 }
1499 // callback might return a promise
1500 return fn ();
1501 }
1502
1503 /**
1504 *
1505 * @param {(Promise|undefined)} promise
1506 * @param {string} event
1507 * @return {(Promise|undefined)}
1508 * @private
1509 */
1510
1511 _chainOrCallHooks ( promise , event ) {
1512 let result = promise;
1513 const hooks = [];
1514 this . _getCommandAndAncestors ()
1515 . reverse ()
1516 . filter (( cmd ) => cmd._lifeCycleHooks[event] !== undefined )
1517 . forEach (( hookedCommand ) => {
1518 hookedCommand._lifeCycleHooks[event]. forEach (( callback ) => {
1519 hooks. push ({ hookedCommand, callback });
1520 });
1521 });
1522 if (event === 'postAction' ) {
1523 hooks. reverse ();
1524 }
1525
1526 hooks. forEach (( hookDetail ) => {
1527 result = this . _chainOrCall (result, () => {
1528 return hookDetail. callback (hookDetail.hookedCommand, this );
1529 });
1530 });
1531 return result;
1532 }
1533
1534 /**
1535 *
1536 * @param {(Promise|undefined)} promise
1537 * @param {Command} subCommand
1538 * @param {string} event
1539 * @return {(Promise|undefined)}
1540 * @private
1541 */
1542
1543 _chainOrCallSubCommandHook ( promise , subCommand , event ) {
1544 let result = promise;
1545 if ( this ._lifeCycleHooks[event] !== undefined ) {
1546 this ._lifeCycleHooks[event]. forEach (( hook ) => {
1547 result = this . _chainOrCall (result, () => {
1548 return hook ( this , subCommand);
1549 });
1550 });
1551 }
1552 return result;
1553 }
1554
1555 /**
1556 * Process arguments in context of this command.
1557 * Returns action result, in case it is a promise.
1558 *
1559 * @private
1560 */
1561
1562 _parseCommand ( operands , unknown ) {
1563 const parsed = this . parseOptions (unknown);
1564 this . _parseOptionsEnv (); // after cli, so parseArg not called on both cli and env
1565 this . _parseOptionsImplied ();
1566 operands = operands. concat (parsed.operands);
1567 unknown = parsed.unknown;
1568 this .args = operands. concat (unknown);
1569
1570 if (operands && this . _findCommand (operands[ 0 ])) {
1571 return this . _dispatchSubcommand (operands[ 0 ], operands. slice ( 1 ), unknown);
1572 }
1573 if (
1574 this . _getHelpCommand () &&
1575 operands[ 0 ] === this . _getHelpCommand (). name ()
1576 ) {
1577 return this . _dispatchHelpCommand (operands[ 1 ]);
1578 }
1579 if ( this ._defaultCommandName) {
1580 this . _outputHelpIfRequested (unknown); // Run the help for default command from parent rather than passing to default command
1581 return this . _dispatchSubcommand (
1582 this ._defaultCommandName,
1583 operands,
1584 unknown,
1585 );
1586 }
1587 if (
1588 this .commands. length &&
1589 this .args. length === 0 &&
1590 ! this ._actionHandler &&
1591 ! this ._defaultCommandName
1592 ) {
1593 // probably missing subcommand and no handler, user needs help (and exit)
1594 this . help ({ error: true });
1595 }
1596
1597 this . _outputHelpIfRequested (parsed.unknown);
1598 this . _checkForMissingMandatoryOptions ();
1599 this . _checkForConflictingOptions ();
1600
1601 // We do not always call this check to avoid masking a "better" error, like unknown command.
1602 const checkForUnknownOptions = () => {
1603 if (parsed.unknown. length > 0 ) {
1604 this . unknownOption (parsed.unknown[ 0 ]);
1605 }
1606 };
1607
1608 const commandEvent = `command:${ this . name () }` ;
1609 if ( this ._actionHandler) {
1610 checkForUnknownOptions ();
1611 this . _processArguments ();
1612
1613 let promiseChain;
1614 promiseChain = this . _chainOrCallHooks (promiseChain, 'preAction' );
1615 promiseChain = this . _chainOrCall (promiseChain, () =>
1616 this . _actionHandler ( this .processedArgs),
1617 );
1618 if ( this .parent) {
1619 promiseChain = this . _chainOrCall (promiseChain, () => {
1620 this .parent. emit (commandEvent, operands, unknown); // legacy
1621 });
1622 }
1623 promiseChain = this . _chainOrCallHooks (promiseChain, 'postAction' );
1624 return promiseChain;
1625 }
1626 if ( this .parent?. listenerCount (commandEvent)) {
1627 checkForUnknownOptions ();
1628 this . _processArguments ();
1629 this .parent. emit (commandEvent, operands, unknown); // legacy
1630 } else if (operands. length ) {
1631 if ( this . _findCommand ( '*' )) {
1632 // legacy default command
1633 return this . _dispatchSubcommand ( '*' , operands, unknown);
1634 }
1635 if ( this . listenerCount ( 'command:*' )) {
1636 // skip option check, emit event for possible misspelling suggestion
1637 this . emit ( 'command:*' , operands, unknown);
1638 } else if ( this .commands. length ) {
1639 this . unknownCommand ();
1640 } else {
1641 checkForUnknownOptions ();
1642 this . _processArguments ();
1643 }
1644 } else if ( this .commands. length ) {
1645 checkForUnknownOptions ();
1646 // This command has subcommands and nothing hooked up at this level, so display help (and exit).
1647 this . help ({ error: true });
1648 } else {
1649 checkForUnknownOptions ();
1650 this . _processArguments ();
1651 // fall through for caller to handle after calling .parse()
1652 }
1653 }
1654
1655 /**
1656 * Find matching command.
1657 *
1658 * @private
1659 * @return {Command | undefined}
1660 */
1661 _findCommand ( name ) {
1662 if ( ! name) return undefined ;
1663 return this .commands. find (
1664 ( cmd ) => cmd._name === name || cmd._aliases. includes (name),
1665 );
1666 }
1667
1668 /**
1669 * Return an option matching `arg` if any.
1670 *
1671 * @param {string} arg
1672 * @return {Option}
1673 * @package
1674 */
1675
1676 _findOption ( arg ) {
1677 return this .options. find (( option ) => option. is (arg));
1678 }
1679
1680 /**
1681 * Display an error message if a mandatory option does not have a value.
1682 * Called after checking for help flags in leaf subcommand.
1683 *
1684 * @private
1685 */
1686
1687 _checkForMissingMandatoryOptions () {
1688 // Walk up hierarchy so can call in subcommand after checking for displaying help.
1689 this . _getCommandAndAncestors (). forEach (( cmd ) => {
1690 cmd.options. forEach (( anOption ) => {
1691 if (
1692 anOption.mandatory &&
1693 cmd. getOptionValue (anOption. attributeName ()) === undefined
1694 ) {
1695 cmd. missingMandatoryOptionValue (anOption);
1696 }
1697 });
1698 });
1699 }
1700
1701 /**
1702 * Display an error message if conflicting options are used together in this.
1703 *
1704 * @private
1705 */
1706 _checkForConflictingLocalOptions () {
1707 const definedNonDefaultOptions = this .options. filter (( option ) => {
1708 const optionKey = option. attributeName ();
1709 if ( this . getOptionValue (optionKey) === undefined ) {
1710 return false ;
1711 }
1712 return this . getOptionValueSource (optionKey) !== 'default' ;
1713 });
1714
1715 const optionsWithConflicting = definedNonDefaultOptions. filter (
1716 ( option ) => option.conflictsWith. length > 0 ,
1717 );
1718
1719 optionsWithConflicting. forEach (( option ) => {
1720 const conflictingAndDefined = definedNonDefaultOptions. find (( defined ) =>
1721 option.conflictsWith. includes (defined. attributeName ()),
1722 );
1723 if (conflictingAndDefined) {
1724 this . _conflictingOption (option, conflictingAndDefined);
1725 }
1726 });
1727 }
1728
1729 /**
1730 * Display an error message if conflicting options are used together.
1731 * Called after checking for help flags in leaf subcommand.
1732 *
1733 * @private
1734 */
1735 _checkForConflictingOptions () {
1736 // Walk up hierarchy so can call in subcommand after checking for displaying help.
1737 this . _getCommandAndAncestors (). forEach (( cmd ) => {
1738 cmd. _checkForConflictingLocalOptions ();
1739 });
1740 }
1741
1742 /**
1743 * Parse options from `argv` removing known options,
1744 * and return argv split into operands and unknown arguments.
1745 *
1746 * Side effects: modifies command by storing options. Does not reset state if called again.
1747 *
1748 * Examples:
1749 *
1750 * argv => operands, unknown
1751 * --known kkk op => [op], []
1752 * op --known kkk => [op], []
1753 * sub --unknown uuu op => [sub], [--unknown uuu op]
1754 * sub -- --unknown uuu op => [sub --unknown uuu op], []
1755 *
1756 * @param {string[]} args
1757 * @return {{operands: string[], unknown: string[]}}
1758 */
1759
1760 parseOptions ( args ) {
1761 const operands = []; // operands, not options or values
1762 const unknown = []; // first unknown option and remaining unknown args
1763 let dest = operands;
1764
1765 function maybeOption ( arg ) {
1766 return arg. length > 1 && arg[ 0 ] === '-' ;
1767 }
1768
1769 const negativeNumberArg = ( arg ) => {
1770 // return false if not a negative number
1771 if ( ! / ^ -( \d +| \d * \. \d + )(e [+-] ? \d + ) ?$ / . test (arg)) return false ;
1772 // negative number is ok unless digit used as an option in command hierarchy
1773 return ! this . _getCommandAndAncestors (). some (( cmd ) =>
1774 cmd.options
1775 . map (( opt ) => opt.short)
1776 . some (( short ) => / ^ - \d $ / . test (short)),
1777 );
1778 };
1779
1780 // parse options
1781 let activeVariadicOption = null ;
1782 let activeGroup = null ; // working through group of short options, like -abc
1783 let i = 0 ;
1784 while (i < args. length || activeGroup) {
1785 const arg = activeGroup ?? args[i ++ ];
1786 activeGroup = null ;
1787
1788 // literal
1789 if (arg === '--' ) {
1790 if (dest === unknown) dest. push (arg);
1791 dest. push ( ... args. slice (i));
1792 break ;
1793 }
1794
1795 if (
1796 activeVariadicOption &&
1797 ( ! maybeOption (arg) || negativeNumberArg (arg))
1798 ) {
1799 this . emit ( `option:${ activeVariadicOption . name () }` , arg);
1800 continue ;
1801 }
1802 activeVariadicOption = null ;
1803
1804 if ( maybeOption (arg)) {
1805 const option = this . _findOption (arg);
1806 // recognised option, call listener to assign value with possible custom processing
1807 if (option) {
1808 if (option.required) {
1809 const value = args[i ++ ];
1810 if (value === undefined ) this . optionMissingArgument (option);
1811 this . emit ( `option:${ option . name () }` , value);
1812 } else if (option.optional) {
1813 let value = null ;
1814 // historical behaviour is optional value is following arg unless an option
1815 if (
1816 i < args. length &&
1817 ( ! maybeOption (args[i]) || negativeNumberArg (args[i]))
1818 ) {
1819 value = args[i ++ ];
1820 }
1821 this . emit ( `option:${ option . name () }` , value);
1822 } else {
1823 // boolean flag
1824 this . emit ( `option:${ option . name () }` );
1825 }
1826 activeVariadicOption = option.variadic ? option : null ;
1827 continue ;
1828 }
1829 }
1830
1831 // Look for combo options following single dash, eat first one if known.
1832 if (arg. length > 2 && arg[ 0 ] === '-' && arg[ 1 ] !== '-' ) {
1833 const option = this . _findOption ( `-${ arg [ 1 ] }` );
1834 if (option) {
1835 if (
1836 option.required ||
1837 (option.optional && this ._combineFlagAndOptionalValue)
1838 ) {
1839 // option with value following in same argument
1840 this . emit ( `option:${ option . name () }` , arg. slice ( 2 ));
1841 } else {
1842 // boolean option
1843 this . emit ( `option:${ option . name () }` );
1844 // remove the processed option and keep processing group
1845 activeGroup = `-${ arg . slice ( 2 ) }` ;
1846 }
1847 continue ;
1848 }
1849 }
1850
1851 // Look for known long flag with value, like --foo=bar
1852 if ( / ^ -- [ ^ =] + =/ . test (arg)) {
1853 const index = arg. indexOf ( '=' );
1854 const option = this . _findOption (arg. slice ( 0 , index));
1855 if (option && (option.required || option.optional)) {
1856 this . emit ( `option:${ option . name () }` , arg. slice (index + 1 ));
1857 continue ;
1858 }
1859 }
1860
1861 // Not a recognised option by this command.
1862 // Might be a command-argument, or subcommand option, or unknown option, or help command or option.
1863
1864 // An unknown option means further arguments also classified as unknown so can be reprocessed by subcommands.
1865 // A negative number in a leaf command is not an unknown option.
1866 if (
1867 dest === operands &&
1868 maybeOption (arg) &&
1869 ! ( this .commands. length === 0 && negativeNumberArg (arg))
1870 ) {
1871 dest = unknown;
1872 }
1873
1874 // If using positionalOptions, stop processing our options at subcommand.
1875 if (
1876 ( this ._enablePositionalOptions || this ._passThroughOptions) &&
1877 operands. length === 0 &&
1878 unknown. length === 0
1879 ) {
1880 if ( this . _findCommand (arg)) {
1881 operands. push (arg);
1882 unknown. push ( ... args. slice (i));
1883 break ;
1884 } else if (
1885 this . _getHelpCommand () &&
1886 arg === this . _getHelpCommand (). name ()
1887 ) {
1888 operands. push (arg, ... args. slice (i));
1889 break ;
1890 } else if ( this ._defaultCommandName) {
1891 unknown. push (arg, ... args. slice (i));
1892 break ;
1893 }
1894 }
1895
1896 // If using passThroughOptions, stop processing options at first command-argument.
1897 if ( this ._passThroughOptions) {
1898 dest. push (arg, ... args. slice (i));
1899 break ;
1900 }
1901
1902 // add arg
1903 dest. push (arg);
1904 }
1905
1906 return { operands, unknown };
1907 }
1908
1909 /**
1910 * Return an object containing local option values as key-value pairs.
1911 *
1912 * @return {object}
1913 */
1914 opts () {
1915 if ( this ._storeOptionsAsProperties) {
1916 // Preserve original behaviour so backwards compatible when still using properties
1917 const result = {};
1918 const len = this .options. length ;
1919
1920 for ( let i = 0 ; i < len; i ++ ) {
1921 const key = this .options[i]. attributeName ();
1922 result[key] =
1923 key === this ._versionOptionName ? this ._version : this [key];
1924 }
1925 return result;
1926 }
1927
1928 return this ._optionValues;
1929 }
1930
1931 /**
1932 * Return an object containing merged local and global option values as key-value pairs.
1933 *
1934 * @return {object}
1935 */
1936 optsWithGlobals () {
1937 // globals overwrite locals
1938 return this . _getCommandAndAncestors (). reduce (
1939 ( combinedOptions , cmd ) => Object. assign (combinedOptions, cmd. opts ()),
1940 {},
1941 );
1942 }
1943
1944 /**
1945 * Display error message and exit (or call exitOverride).
1946 *
1947 * @param {string} message
1948 * @param {object} [errorOptions]
1949 * @param {string} [errorOptions.code] - an id string representing the error
1950 * @param {number} [errorOptions.exitCode] - used with process.exit
1951 */
1952 error ( message , errorOptions ) {
1953 // output handling
1954 this ._outputConfiguration. outputError (
1955 `${ message } \n ` ,
1956 this ._outputConfiguration.writeErr,
1957 );
1958 if ( typeof this ._showHelpAfterError === 'string' ) {
1959 this ._outputConfiguration. writeErr ( `${ this . _showHelpAfterError } \n ` );
1960 } else if ( this ._showHelpAfterError) {
1961 this ._outputConfiguration. writeErr ( ' \n ' );
1962 this . outputHelp ({ error: true });
1963 }
1964
1965 // exit handling
1966 const config = errorOptions || {};
1967 const exitCode = config.exitCode || 1 ;
1968 const code = config.code || 'commander.error' ;
1969 this . _exit (exitCode, code, message);
1970 }
1971
1972 /**
1973 * Apply any option related environment variables, if option does
1974 * not have a value from cli or client code.
1975 *
1976 * @private
1977 */
1978 _parseOptionsEnv () {
1979 this .options. forEach (( option ) => {
1980 if (option.envVar && option.envVar in process.env) {
1981 const optionKey = option. attributeName ();
1982 // Priority check. Do not overwrite cli or options from unknown source (client-code).
1983 if (
1984 this . getOptionValue (optionKey) === undefined ||
1985 [ 'default' , 'config' , 'env' ]. includes (
1986 this . getOptionValueSource (optionKey),
1987 )
1988 ) {
1989 if (option.required || option.optional) {
1990 // option can take a value
1991 // keep very simple, optional always takes value
1992 this . emit ( `optionEnv:${ option . name () }` , process.env[option.envVar]);
1993 } else {
1994 // boolean
1995 // keep very simple, only care that envVar defined and not the value
1996 this . emit ( `optionEnv:${ option . name () }` );
1997 }
1998 }
1999 }
2000 });
2001 }
2002
2003 /**
2004 * Apply any implied option values, if option is undefined or default value.
2005 *
2006 * @private
2007 */
2008 _parseOptionsImplied () {
2009 const dualHelper = new DualOptions ( this .options);
2010 const hasCustomOptionValue = ( optionKey ) => {
2011 return (
2012 this . getOptionValue (optionKey) !== undefined &&
2013 ! [ 'default' , 'implied' ]. includes ( this . getOptionValueSource (optionKey))
2014 );
2015 };
2016 this .options
2017 . filter (
2018 ( option ) =>
2019 option.implied !== undefined &&
2020 hasCustomOptionValue (option. attributeName ()) &&
2021 dualHelper. valueFromOption (
2022 this . getOptionValue (option. attributeName ()),
2023 option,
2024 ),
2025 )
2026 . forEach (( option ) => {
2027 Object. keys (option.implied)
2028 . filter (( impliedKey ) => ! hasCustomOptionValue (impliedKey))
2029 . forEach (( impliedKey ) => {
2030 this . setOptionValueWithSource (
2031 impliedKey,
2032 option.implied[impliedKey],
2033 'implied' ,
2034 );
2035 });
2036 });
2037 }
2038
2039 /**
2040 * Argument `name` is missing.
2041 *
2042 * @param {string} name
2043 * @private
2044 */
2045
2046 missingArgument ( name ) {
2047 const message = `error: missing required argument '${ name }'` ;
2048 this . error (message, { code: 'commander.missingArgument' });
2049 }
2050
2051 /**
2052 * `Option` is missing an argument.
2053 *
2054 * @param {Option} option
2055 * @private
2056 */
2057
2058 optionMissingArgument ( option ) {
2059 const message = `error: option '${ option . flags }' argument missing` ;
2060 this . error (message, { code: 'commander.optionMissingArgument' });
2061 }
2062
2063 /**
2064 * `Option` does not have a value, and is a mandatory option.
2065 *
2066 * @param {Option} option
2067 * @private
2068 */
2069
2070 missingMandatoryOptionValue ( option ) {
2071 const message = `error: required option '${ option . flags }' not specified` ;
2072 this . error (message, { code: 'commander.missingMandatoryOptionValue' });
2073 }
2074
2075 /**
2076 * `Option` conflicts with another option.
2077 *
2078 * @param {Option} option
2079 * @param {Option} conflictingOption
2080 * @private
2081 */
2082 _conflictingOption ( option , conflictingOption ) {
2083 // The calling code does not know whether a negated option is the source of the
2084 // value, so do some work to take an educated guess.
2085 const findBestOptionFromValue = ( option ) => {
2086 const optionKey = option. attributeName ();
2087 const optionValue = this . getOptionValue (optionKey);
2088 const negativeOption = this .options. find (
2089 ( target ) => target.negate && optionKey === target. attributeName (),
2090 );
2091 const positiveOption = this .options. find (
2092 ( target ) => ! target.negate && optionKey === target. attributeName (),
2093 );
2094 if (
2095 negativeOption &&
2096 ((negativeOption.presetArg === undefined && optionValue === false ) ||
2097 (negativeOption.presetArg !== undefined &&
2098 optionValue === negativeOption.presetArg))
2099 ) {
2100 return negativeOption;
2101 }
2102 return positiveOption || option;
2103 };
2104
2105 const getErrorMessage = ( option ) => {
2106 const bestOption = findBestOptionFromValue (option);
2107 const optionKey = bestOption. attributeName ();
2108 const source = this . getOptionValueSource (optionKey);
2109 if (source === 'env' ) {
2110 return `environment variable '${ bestOption . envVar }'` ;
2111 }
2112 return `option '${ bestOption . flags }'` ;
2113 };
2114
2115 const message = `error: ${ getErrorMessage ( option ) } cannot be used with ${ getErrorMessage ( conflictingOption ) }` ;
2116 this . error (message, { code: 'commander.conflictingOption' });
2117 }
2118
2119 /**
2120 * Unknown option `flag`.
2121 *
2122 * @param {string} flag
2123 * @private
2124 */
2125
2126 unknownOption ( flag ) {
2127 if ( this ._allowUnknownOption) return ;
2128 let suggestion = '' ;
2129
2130 if (flag. startsWith ( '--' ) && this ._showSuggestionAfterError) {
2131 // Looping to pick up the global options too
2132 let candidateFlags = [];
2133 // eslint-disable-next-line @typescript-eslint/no-this-alias
2134 let command = this ;
2135 do {
2136 const moreFlags = command
2137 . createHelp ()
2138 . visibleOptions (command)
2139 . filter (( option ) => option.long)
2140 . map (( option ) => option.long);
2141 candidateFlags = candidateFlags. concat (moreFlags);
2142 command = command.parent;
2143 } while (command && ! command._enablePositionalOptions);
2144 suggestion = suggestSimilar (flag, candidateFlags);
2145 }
2146
2147 const message = `error: unknown option '${ flag }'${ suggestion }` ;
2148 this . error (message, { code: 'commander.unknownOption' });
2149 }
2150
2151 /**
2152 * Excess arguments, more than expected.
2153 *
2154 * @param {string[]} receivedArgs
2155 * @private
2156 */
2157
2158 _excessArguments ( receivedArgs ) {
2159 if ( this ._allowExcessArguments) return ;
2160
2161 const expected = this .registeredArguments. length ;
2162 const s = expected === 1 ? '' : 's' ;
2163 const received = receivedArgs. length ;
2164 const forSubcommand = this .parent ? ` for '${ this . name () }'` : '' ;
2165 const details = receivedArgs. join ( ', ' );
2166 const message = `error: too many arguments${ forSubcommand }. Expected ${ expected } argument${ s } but got ${ received }: ${ details }.` ;
2167 this . error (message, { code: 'commander.excessArguments' });
2168 }
2169
2170 /**
2171 * Unknown command.
2172 *
2173 * @private
2174 */
2175
2176 unknownCommand () {
2177 const unknownName = this .args[ 0 ];
2178 let suggestion = '' ;
2179
2180 if ( this ._showSuggestionAfterError) {
2181 const candidateNames = [];
2182 this . createHelp ()
2183 . visibleCommands ( this )
2184 . forEach (( command ) => {
2185 candidateNames. push (command. name ());
2186 // just visible alias
2187 if (command. alias ()) candidateNames. push (command. alias ());
2188 });
2189 suggestion = suggestSimilar (unknownName, candidateNames);
2190 }
2191
2192 const message = `error: unknown command '${ unknownName }'${ suggestion }` ;
2193 this . error (message, { code: 'commander.unknownCommand' });
2194 }
2195
2196 /**
2197 * Get or set the program version.
2198 *
2199 * This method auto-registers the "-V, --version" option which will print the version number.
2200 *
2201 * You can optionally supply the flags and description to override the defaults.
2202 *
2203 * @param {string} [str]
2204 * @param {string} [flags]
2205 * @param {string} [description]
2206 * @return {(this | string | undefined)} `this` command for chaining, or version string if no arguments
2207 */
2208
2209 version ( str , flags , description ) {
2210 if (str === undefined ) return this ._version;
2211 this ._version = str;
2212 flags = flags || '-V, --version' ;
2213 description = description || 'output the version number' ;
2214 const versionOption = this . createOption (flags, description);
2215 this ._versionOptionName = versionOption. attributeName ();
2216 this . _registerOption (versionOption);
2217
2218 this . on ( 'option:' + versionOption. name (), () => {
2219 this ._outputConfiguration. writeOut ( `${ str } \n ` );
2220 this . _exit ( 0 , 'commander.version' , str);
2221 });
2222 return this ;
2223 }
2224
2225 /**
2226 * Set the description.
2227 *
2228 * @param {string} [str]
2229 * @param {object} [argsDescription]
2230 * @return {(string|Command)}
2231 */
2232 description ( str , argsDescription ) {
2233 if (str === undefined && argsDescription === undefined )
2234 return this ._description;
2235 this ._description = str;
2236 if (argsDescription) {
2237 this ._argsDescription = argsDescription;
2238 }
2239 return this ;
2240 }
2241
2242 /**
2243 * Set the summary. Used when listed as subcommand of parent.
2244 *
2245 * @param {string} [str]
2246 * @return {(string|Command)}
2247 */
2248 summary ( str ) {
2249 if (str === undefined ) return this ._summary;
2250 this ._summary = str;
2251 return this ;
2252 }
2253
2254 /**
2255 * Set an alias for the command.
2256 *
2257 * You may call more than once to add multiple aliases. Only the first alias is shown in the auto-generated help.
2258 *
2259 * @param {string} [alias]
2260 * @return {(string|Command)}
2261 */
2262
2263 alias ( alias ) {
2264 if (alias === undefined ) return this ._aliases[ 0 ]; // just return first, for backwards compatibility
2265
2266 /** @type {Command} */
2267 // eslint-disable-next-line @typescript-eslint/no-this-alias
2268 let command = this ;
2269 if (
2270 this .commands. length !== 0 &&
2271 this .commands[ this .commands. length - 1 ]._executableHandler
2272 ) {
2273 // assume adding alias for last added executable subcommand, rather than this
2274 command = this .commands[ this .commands. length - 1 ];
2275 }
2276
2277 if (alias === command._name)
2278 throw new Error ( "Command alias can't be the same as its name" );
2279 const matchingCommand = this .parent?. _findCommand (alias);
2280 if (matchingCommand) {
2281 // c.f. _registerCommand
2282 const existingCmd = [matchingCommand. name ()]
2283 . concat (matchingCommand. aliases ())
2284 . join ( '|' );
2285 throw new Error (
2286 `cannot add alias '${ alias }' to command '${ this . name () }' as already have command '${ existingCmd }'` ,
2287 );
2288 }
2289
2290 command._aliases. push (alias);
2291 return this ;
2292 }
2293
2294 /**
2295 * Set aliases for the command.
2296 *
2297 * Only the first alias is shown in the auto-generated help.
2298 *
2299 * @param {string[]} [aliases]
2300 * @return {(string[]|Command)}
2301 */
2302
2303 aliases ( aliases ) {
2304 // Getter for the array of aliases is the main reason for having aliases() in addition to alias().
2305 if (aliases === undefined ) return this ._aliases;
2306
2307 aliases. forEach (( alias ) => this . alias (alias));
2308 return this ;
2309 }
2310
2311 /**
2312 * Set / get the command usage `str`.
2313 *
2314 * @param {string} [str]
2315 * @return {(string|Command)}
2316 */
2317
2318 usage ( str ) {
2319 if (str === undefined ) {
2320 if ( this ._usage) return this ._usage;
2321
2322 const args = this .registeredArguments. map (( arg ) => {
2323 return humanReadableArgName (arg);
2324 });
2325 return []
2326 . concat (
2327 this .options. length || this ._helpOption !== null ? '[options]' : [],
2328 this .commands. length ? '[command]' : [],
2329 this .registeredArguments. length ? args : [],
2330 )
2331 . join ( ' ' );
2332 }
2333
2334 this ._usage = str;
2335 return this ;
2336 }
2337
2338 /**
2339 * Get or set the name of the command.
2340 *
2341 * @param {string} [str]
2342 * @return {(string|Command)}
2343 */
2344
2345 name ( str ) {
2346 if (str === undefined ) return this ._name;
2347 this ._name = str;
2348 return this ;
2349 }
2350
2351 /**
2352 * Set/get the help group heading for this subcommand in parent command's help.
2353 *
2354 * @param {string} [heading]
2355 * @return {Command | string}
2356 */
2357
2358 helpGroup ( heading ) {
2359 if (heading === undefined ) return this ._helpGroupHeading ?? '' ;
2360 this ._helpGroupHeading = heading;
2361 return this ;
2362 }
2363
2364 /**
2365 * Set/get the default help group heading for subcommands added to this command.
2366 * (This does not override a group set directly on the subcommand using .helpGroup().)
2367 *
2368 * @example
2369 * program.commandsGroup('Development Commands:);
2370 * program.command('watch')...
2371 * program.command('lint')...
2372 * ...
2373 *
2374 * @param {string} [heading]
2375 * @returns {Command | string}
2376 */
2377 commandsGroup ( heading ) {
2378 if (heading === undefined ) return this ._defaultCommandGroup ?? '' ;
2379 this ._defaultCommandGroup = heading;
2380 return this ;
2381 }
2382
2383 /**
2384 * Set/get the default help group heading for options added to this command.
2385 * (This does not override a group set directly on the option using .helpGroup().)
2386 *
2387 * @example
2388 * program
2389 * .optionsGroup('Development Options:')
2390 * .option('-d, --debug', 'output extra debugging')
2391 * .option('-p, --profile', 'output profiling information')
2392 *
2393 * @param {string} [heading]
2394 * @returns {Command | string}
2395 */
2396 optionsGroup ( heading ) {
2397 if (heading === undefined ) return this ._defaultOptionGroup ?? '' ;
2398 this ._defaultOptionGroup = heading;
2399 return this ;
2400 }
2401
2402 /**
2403 * @param {Option} option
2404 * @private
2405 */
2406 _initOptionGroup ( option ) {
2407 if ( this ._defaultOptionGroup && ! option.helpGroupHeading)
2408 option. helpGroup ( this ._defaultOptionGroup);
2409 }
2410
2411 /**
2412 * @param {Command} cmd
2413 * @private
2414 */
2415 _initCommandGroup ( cmd ) {
2416 if ( this ._defaultCommandGroup && ! cmd. helpGroup ())
2417 cmd. helpGroup ( this ._defaultCommandGroup);
2418 }
2419
2420 /**
2421 * Set the name of the command from script filename, such as process.argv[1],
2422 * or import.meta.filename.
2423 *
2424 * (Used internally and public although not documented in README.)
2425 *
2426 * @example
2427 * program.nameFromFilename(import.meta.filename);
2428 *
2429 * @param {string} filename
2430 * @return {Command}
2431 */
2432
2433 nameFromFilename ( filename ) {
2434 this ._name = path. basename (filename, path. extname (filename));
2435
2436 return this ;
2437 }
2438
2439 /**
2440 * Get or set the directory for searching for executable subcommands of this command.
2441 *
2442 * @example
2443 * program.executableDir(import.meta.dirname);
2444 * // or
2445 * program.executableDir('subcommands');
2446 *
2447 * @param {string} [path]
2448 * @return {(string|null|Command)}
2449 */
2450
2451 executableDir ( path ) {
2452 if (path === undefined ) return this ._executableDir;
2453 this ._executableDir = path;
2454 return this ;
2455 }
2456
2457 /**
2458 * Return program help documentation.
2459 *
2460 * @param {{ error: boolean }} [contextOptions] - pass {error:true} to wrap for stderr instead of stdout
2461 * @return {string}
2462 */
2463
2464 helpInformation ( contextOptions ) {
2465 const helper = this . createHelp ();
2466 const context = this . _getOutputContext (contextOptions);
2467 helper. prepareContext ({
2468 error: context.error,
2469 helpWidth: context.helpWidth,
2470 outputHasColors: context.hasColors,
2471 });
2472 const text = helper. formatHelp ( this , helper);
2473 if (context.hasColors) return text;
2474 return this ._outputConfiguration. stripColor (text);
2475 }
2476
2477 /**
2478 * @typedef HelpContext
2479 * @type {object}
2480 * @property {boolean} error
2481 * @property {number} helpWidth
2482 * @property {boolean} hasColors
2483 * @property {function} write - includes stripColor if needed
2484 *
2485 * @returns {HelpContext}
2486 * @private
2487 */
2488
2489 _getOutputContext ( contextOptions ) {
2490 contextOptions = contextOptions || {};
2491 const error = !! contextOptions.error;
2492 let baseWrite;
2493 let hasColors;
2494 let helpWidth;
2495 if (error) {
2496 baseWrite = ( str ) => this ._outputConfiguration. writeErr (str);
2497 hasColors = this ._outputConfiguration. getErrHasColors ();
2498 helpWidth = this ._outputConfiguration. getErrHelpWidth ();
2499 } else {
2500 baseWrite = ( str ) => this ._outputConfiguration. writeOut (str);
2501 hasColors = this ._outputConfiguration. getOutHasColors ();
2502 helpWidth = this ._outputConfiguration. getOutHelpWidth ();
2503 }
2504 const write = ( str ) => {
2505 if ( ! hasColors) str = this ._outputConfiguration. stripColor (str);
2506 return baseWrite (str);
2507 };
2508 return { error, write, hasColors, helpWidth };
2509 }
2510
2511 /**
2512 * Output help information for this command.
2513 *
2514 * Outputs built-in help, and custom text added using `.addHelpText()`.
2515 *
2516 * @param {{ error: boolean } | Function} [contextOptions] - pass {error:true} to write to stderr instead of stdout
2517 */
2518
2519 outputHelp ( contextOptions ) {
2520 let deprecatedCallback;
2521 if ( typeof contextOptions === 'function' ) {
2522 deprecatedCallback = contextOptions;
2523 contextOptions = undefined ;
2524 }
2525
2526 const outputContext = this . _getOutputContext (contextOptions);
2527 /** @type {HelpTextEventContext} */
2528 const eventContext = {
2529 error: outputContext.error,
2530 write: outputContext.write,
2531 command: this ,
2532 };
2533
2534 this . _getCommandAndAncestors ()
2535 . reverse ()
2536 . forEach (( command ) => command. emit ( 'beforeAllHelp' , eventContext));
2537 this . emit ( 'beforeHelp' , eventContext);
2538
2539 let helpInformation = this . helpInformation ({ error: outputContext.error });
2540 if (deprecatedCallback) {
2541 helpInformation = deprecatedCallback (helpInformation);
2542 if (
2543 typeof helpInformation !== 'string' &&
2544 ! Buffer. isBuffer (helpInformation)
2545 ) {
2546 throw new Error ( 'outputHelp callback must return a string or a Buffer' );
2547 }
2548 }
2549 outputContext. write (helpInformation);
2550
2551 if ( this . _getHelpOption ()?.long) {
2552 this . emit ( this . _getHelpOption ().long); // deprecated
2553 }
2554 this . emit ( 'afterHelp' , eventContext);
2555 this . _getCommandAndAncestors (). forEach (( command ) =>
2556 command. emit ( 'afterAllHelp' , eventContext),
2557 );
2558 }
2559
2560 /**
2561 * You can pass in flags and a description to customise the built-in help option.
2562 * Pass in false to disable the built-in help option.
2563 *
2564 * @example
2565 * program.helpOption('-?, --help' 'show help'); // customise
2566 * program.helpOption(false); // disable
2567 *
2568 * @param {(string | boolean)} flags
2569 * @param {string} [description]
2570 * @return {Command} `this` command for chaining
2571 */
2572
2573 helpOption ( flags , description ) {
2574 // Support enabling/disabling built-in help option.
2575 if ( typeof flags === 'boolean' ) {
2576 if (flags) {
2577 if ( this ._helpOption === null ) this ._helpOption = undefined ; // reenable
2578 if ( this ._defaultOptionGroup) {
2579 // make the option to store the group
2580 this . _initOptionGroup ( this . _getHelpOption ());
2581 }
2582 } else {
2583 this ._helpOption = null ; // disable
2584 }
2585 return this ;
2586 }
2587
2588 // Customise flags and description.
2589 this ._helpOption = this . createOption (
2590 flags ?? '-h, --help' ,
2591 description ?? 'display help for command' ,
2592 );
2593 // init group unless lazy create
2594 if (flags || description) this . _initOptionGroup ( this ._helpOption);
2595
2596 return this ;
2597 }
2598
2599 /**
2600 * Lazy create help option.
2601 * Returns null if has been disabled with .helpOption(false).
2602 *
2603 * @returns {(Option | null)} the help option
2604 * @package
2605 */
2606 _getHelpOption () {
2607 // Lazy create help option on demand.
2608 if ( this ._helpOption === undefined ) {
2609 this . helpOption ( undefined , undefined );
2610 }
2611 return this ._helpOption;
2612 }
2613
2614 /**
2615 * Supply your own option to use for the built-in help option.
2616 * This is an alternative to using helpOption() to customise the flags and description etc.
2617 *
2618 * @param {Option} option
2619 * @return {Command} `this` command for chaining
2620 */
2621 addHelpOption ( option ) {
2622 this ._helpOption = option;
2623 this . _initOptionGroup (option);
2624 return this ;
2625 }
2626
2627 /**
2628 * Output help information and exit.
2629 *
2630 * Outputs built-in help, and custom text added using `.addHelpText()`.
2631 *
2632 * @param {{ error: boolean }} [contextOptions] - pass {error:true} to write to stderr instead of stdout
2633 */
2634
2635 help ( contextOptions ) {
2636 this . outputHelp (contextOptions);
2637 let exitCode = Number (process.exitCode ?? 0 ); // process.exitCode does allow a string or an integer, but we prefer just a number
2638 if (
2639 exitCode === 0 &&
2640 contextOptions &&
2641 typeof contextOptions !== 'function' &&
2642 contextOptions.error
2643 ) {
2644 exitCode = 1 ;
2645 }
2646 // message: do not have all displayed text available so only passing placeholder.
2647 this . _exit (exitCode, 'commander.help' , '(outputHelp)' );
2648 }
2649
2650 /**
2651 * // Do a little typing to coordinate emit and listener for the help text events.
2652 * @typedef HelpTextEventContext
2653 * @type {object}
2654 * @property {boolean} error
2655 * @property {Command} command
2656 * @property {function} write
2657 */
2658
2659 /**
2660 * Add additional text to be displayed with the built-in help.
2661 *
2662 * Position is 'before' or 'after' to affect just this command,
2663 * and 'beforeAll' or 'afterAll' to affect this command and all its subcommands.
2664 *
2665 * @param {string} position - before or after built-in help
2666 * @param {(string | Function)} text - string to add, or a function returning a string
2667 * @return {Command} `this` command for chaining
2668 */
2669
2670 addHelpText ( position , text ) {
2671 const allowedValues = [ 'beforeAll' , 'before' , 'after' , 'afterAll' ];
2672 if ( ! allowedValues. includes (position)) {
2673 throw new Error ( `Unexpected value for position to addHelpText.
2674 Expecting one of '${ allowedValues . join ( "', '" ) }'` );
2675 }
2676
2677 const helpEvent = `${ position }Help` ;
2678 this . on (helpEvent, ( /** @type {HelpTextEventContext} */ context ) => {
2679 let helpStr;
2680 if ( typeof text === 'function' ) {
2681 helpStr = text ({ error: context.error, command: context.command });
2682 } else {
2683 helpStr = text;
2684 }
2685 // Ignore falsy value when nothing to output.
2686 if (helpStr) {
2687 context. write ( `${ helpStr } \n ` );
2688 }
2689 });
2690 return this ;
2691 }
2692
2693 /**
2694 * Output help information if help flags specified
2695 *
2696 * @param {Array} args - array of options to search for help flags
2697 * @private
2698 */
2699
2700 _outputHelpIfRequested ( args ) {
2701 const helpOption = this . _getHelpOption ();
2702 const helpRequested = helpOption && args. find (( arg ) => helpOption. is (arg));
2703 if (helpRequested) {
2704 this . outputHelp ();
2705 // (Do not have all displayed text available so only passing placeholder.)
2706 this . _exit ( 0 , 'commander.helpDisplayed' , '(outputHelp)' );
2707 }
2708 }
2709 }
2710
2711 /**
2712 * Scan arguments and increment port number for inspect calls (to avoid conflicts when spawning new command).
2713 *
2714 * @param {string[]} args - array of arguments from node.execArgv
2715 * @returns {string[]}
2716 * @private
2717 */
2718
2719 function incrementNodeInspectorPort ( args ) {
2720 // Testing for these options:
2721 // --inspect[=[host:]port]
2722 // --inspect-brk[=[host:]port]
2723 // --inspect-port=[host:]port
2724 return args. map (( arg ) => {
2725 if ( ! arg. startsWith ( '--inspect' )) {
2726 return arg;
2727 }
2728 let debugOption;
2729 let debugHost = '127.0.0.1' ;
2730 let debugPort = '9229' ;
2731 let match;
2732 if ((match = arg. match ( / ^ (--inspect(-brk) ? ) $ / )) !== null ) {
2733 // e.g. --inspect
2734 debugOption = match[ 1 ];
2735 } else if (
2736 (match = arg. match ( / ^ (--inspect(-brk | -port) ? )=( [ ^ :] + ) $ / )) !== null
2737 ) {
2738 debugOption = match[ 1 ];
2739 if ( / ^ \d +$ / . test (match[ 3 ])) {
2740 // e.g. --inspect=1234
2741 debugPort = match[ 3 ];
2742 } else {
2743 // e.g. --inspect=localhost
2744 debugHost = match[ 3 ];
2745 }
2746 } else if (
2747 (match = arg. match ( / ^ (--inspect(-brk | -port) ? )=( [ ^ :] + ):( \d + ) $ / )) !== null
2748 ) {
2749 // e.g. --inspect=localhost:1234
2750 debugOption = match[ 1 ];
2751 debugHost = match[ 3 ];
2752 debugPort = match[ 4 ];
2753 }
2754
2755 if (debugOption && debugPort !== '0' ) {
2756 return `${ debugOption }=${ debugHost }:${ parseInt ( debugPort ) + 1 }` ;
2757 }
2758 return arg;
2759 });
2760 }
2761
2762 /**
2763 * Exported for using from tests, not otherwise used outside this file.
2764 *
2765 * @returns {boolean | undefined}
2766 * @package
2767 */
2768 export function useColor () {
2769 // Test for common conventions.
2770 // NB: the observed behaviour is in combination with how author adds color! For example:
2771 // - we do not test NODE_DISABLE_COLORS, but util:styletext does
2772 // - we do test NO_COLOR, but Chalk does not
2773 //
2774 // References:
2775 // https://no-color.org
2776 // https://bixense.com/clicolors/
2777 // https://github.com/nodejs/node/blob/0a00217a5f67ef4a22384cfc80eb6dd9a917fdc1/lib/internal/tty.js#L109
2778 // https://github.com/chalk/supports-color/blob/c214314a14bcb174b12b3014b2b0a8de375029ae/index.js#L33
2779 // (https://force-color.org recent web page from 2023, does not match major javascript implementations)
2780
2781 if (
2782 process.env. NO_COLOR ||
2783 process.env. FORCE_COLOR === '0' ||
2784 process.env. FORCE_COLOR === 'false'
2785 )
2786 return false ;
2787 if (process.env. FORCE_COLOR || process.env. CLICOLOR_FORCE !== undefined )
2788 return true ;
2789 return undefined ;
2790 }