• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1const EventEmitter = require('events').EventEmitter;
2const childProcess = require('child_process');
3const path = require('path');
4const fs = require('fs');
5const process = require('process');
6
7const { Argument, humanReadableArgName } = require('./argument.js');
8const { CommanderError } = require('./error.js');
9const { Help } = require('./help.js');
10const { Option, splitOptionFlags, DualOptions } = require('./option.js');
11const { suggestSimilar } = require('./suggestSimilar');
12
13// @ts-check
14
15class Command extends EventEmitter {
16  /**
17   * Initialize a new `Command`.
18   *
19   * @param {string} [name]
20   */
21
22  constructor(name) {
23    super();
24    /** @type {Command[]} */
25    this.commands = [];
26    /** @type {Option[]} */
27    this.options = [];
28    this.parent = null;
29    this._allowUnknownOption = false;
30    this._allowExcessArguments = true;
31    /** @type {Argument[]} */
32    this._args = [];
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
60    // see .configureOutput() for docs
61    this._outputConfiguration = {
62      writeOut: (str) => process.stdout.write(str),
63      writeErr: (str) => process.stderr.write(str),
64      getOutHelpWidth: () => process.stdout.isTTY ? process.stdout.columns : undefined,
65      getErrHelpWidth: () => process.stderr.isTTY ? process.stderr.columns : undefined,
66      outputError: (str, write) => write(str)
67    };
68
69    this._hidden = false;
70    this._hasHelpOption = true;
71    this._helpFlags = '-h, --help';
72    this._helpDescription = 'display help for command';
73    this._helpShortFlag = '-h';
74    this._helpLongFlag = '--help';
75    this._addImplicitHelpCommand = undefined; // Deliberately undefined, not decided whether true or false
76    this._helpCommandName = 'help';
77    this._helpCommandnameAndArgs = 'help [command]';
78    this._helpCommandDescription = 'display help for command';
79    this._helpConfiguration = {};
80  }
81
82  /**
83   * Copy settings that are useful to have in common across root command and subcommands.
84   *
85   * (Used internally when adding a command using `.command()` so subcommands inherit parent settings.)
86   *
87   * @param {Command} sourceCommand
88   * @return {Command} `this` command for chaining
89   */
90  copyInheritedSettings(sourceCommand) {
91    this._outputConfiguration = sourceCommand._outputConfiguration;
92    this._hasHelpOption = sourceCommand._hasHelpOption;
93    this._helpFlags = sourceCommand._helpFlags;
94    this._helpDescription = sourceCommand._helpDescription;
95    this._helpShortFlag = sourceCommand._helpShortFlag;
96    this._helpLongFlag = sourceCommand._helpLongFlag;
97    this._helpCommandName = sourceCommand._helpCommandName;
98    this._helpCommandnameAndArgs = sourceCommand._helpCommandnameAndArgs;
99    this._helpCommandDescription = sourceCommand._helpCommandDescription;
100    this._helpConfiguration = sourceCommand._helpConfiguration;
101    this._exitCallback = sourceCommand._exitCallback;
102    this._storeOptionsAsProperties = sourceCommand._storeOptionsAsProperties;
103    this._combineFlagAndOptionalValue = sourceCommand._combineFlagAndOptionalValue;
104    this._allowExcessArguments = sourceCommand._allowExcessArguments;
105    this._enablePositionalOptions = sourceCommand._enablePositionalOptions;
106    this._showHelpAfterError = sourceCommand._showHelpAfterError;
107    this._showSuggestionAfterError = sourceCommand._showSuggestionAfterError;
108
109    return this;
110  }
111
112  /**
113   * Define a command.
114   *
115   * There are two styles of command: pay attention to where to put the description.
116   *
117   * @example
118   * // Command implemented using action handler (description is supplied separately to `.command`)
119   * program
120   *   .command('clone <source> [destination]')
121   *   .description('clone a repository into a newly created directory')
122   *   .action((source, destination) => {
123   *     console.log('clone command called');
124   *   });
125   *
126   * // Command implemented using separate executable file (description is second parameter to `.command`)
127   * program
128   *   .command('start <service>', 'start named service')
129   *   .command('stop [service]', 'stop named service, or all if no name supplied');
130   *
131   * @param {string} nameAndArgs - command name and arguments, args are `<required>` or `[optional]` and last may also be `variadic...`
132   * @param {Object|string} [actionOptsOrExecDesc] - configuration options (for action), or description (for executable)
133   * @param {Object} [execOpts] - configuration options (for executable)
134   * @return {Command} returns new command for action handler, or `this` for executable command
135   */
136
137  command(nameAndArgs, actionOptsOrExecDesc, execOpts) {
138    let desc = actionOptsOrExecDesc;
139    let opts = execOpts;
140    if (typeof desc === 'object' && desc !== null) {
141      opts = desc;
142      desc = null;
143    }
144    opts = opts || {};
145    const [, name, args] = nameAndArgs.match(/([^ ]+) *(.*)/);
146
147    const cmd = this.createCommand(name);
148    if (desc) {
149      cmd.description(desc);
150      cmd._executableHandler = true;
151    }
152    if (opts.isDefault) this._defaultCommandName = cmd._name;
153    cmd._hidden = !!(opts.noHelp || opts.hidden); // noHelp is deprecated old name for hidden
154    cmd._executableFile = opts.executableFile || null; // Custom name for executable file, set missing to null to match constructor
155    if (args) cmd.arguments(args);
156    this.commands.push(cmd);
157    cmd.parent = this;
158    cmd.copyInheritedSettings(this);
159
160    if (desc) return this;
161    return cmd;
162  }
163
164  /**
165   * Factory routine to create a new unattached command.
166   *
167   * See .command() for creating an attached subcommand, which uses this routine to
168   * create the command. You can override createCommand to customise subcommands.
169   *
170   * @param {string} [name]
171   * @return {Command} new command
172   */
173
174  createCommand(name) {
175    return new Command(name);
176  }
177
178  /**
179   * You can customise the help with a subclass of Help by overriding createHelp,
180   * or by overriding Help properties using configureHelp().
181   *
182   * @return {Help}
183   */
184
185  createHelp() {
186    return Object.assign(new Help(), this.configureHelp());
187  }
188
189  /**
190   * You can customise the help by overriding Help properties using configureHelp(),
191   * or with a subclass of Help by overriding createHelp().
192   *
193   * @param {Object} [configuration] - configuration options
194   * @return {Command|Object} `this` command for chaining, or stored configuration
195   */
196
197  configureHelp(configuration) {
198    if (configuration === undefined) return this._helpConfiguration;
199
200    this._helpConfiguration = configuration;
201    return this;
202  }
203
204  /**
205   * The default output goes to stdout and stderr. You can customise this for special
206   * applications. You can also customise the display of errors by overriding outputError.
207   *
208   * The configuration properties are all functions:
209   *
210   *     // functions to change where being written, stdout and stderr
211   *     writeOut(str)
212   *     writeErr(str)
213   *     // matching functions to specify width for wrapping help
214   *     getOutHelpWidth()
215   *     getErrHelpWidth()
216   *     // functions based on what is being written out
217   *     outputError(str, write) // used for displaying errors, and not used for displaying help
218   *
219   * @param {Object} [configuration] - configuration options
220   * @return {Command|Object} `this` command for chaining, or stored configuration
221   */
222
223  configureOutput(configuration) {
224    if (configuration === undefined) return this._outputConfiguration;
225
226    Object.assign(this._outputConfiguration, configuration);
227    return this;
228  }
229
230  /**
231   * Display the help or a custom message after an error occurs.
232   *
233   * @param {boolean|string} [displayHelp]
234   * @return {Command} `this` command for chaining
235   */
236  showHelpAfterError(displayHelp = true) {
237    if (typeof displayHelp !== 'string') displayHelp = !!displayHelp;
238    this._showHelpAfterError = displayHelp;
239    return this;
240  }
241
242  /**
243   * Display suggestion of similar commands for unknown commands, or options for unknown options.
244   *
245   * @param {boolean} [displaySuggestion]
246   * @return {Command} `this` command for chaining
247   */
248  showSuggestionAfterError(displaySuggestion = true) {
249    this._showSuggestionAfterError = !!displaySuggestion;
250    return this;
251  }
252
253  /**
254   * Add a prepared subcommand.
255   *
256   * See .command() for creating an attached subcommand which inherits settings from its parent.
257   *
258   * @param {Command} cmd - new subcommand
259   * @param {Object} [opts] - configuration options
260   * @return {Command} `this` command for chaining
261   */
262
263  addCommand(cmd, opts) {
264    if (!cmd._name) {
265      throw new Error(`Command passed to .addCommand() must have a name
266- specify the name in Command constructor or using .name()`);
267    }
268
269    opts = opts || {};
270    if (opts.isDefault) this._defaultCommandName = cmd._name;
271    if (opts.noHelp || opts.hidden) cmd._hidden = true; // modifying passed command due to existing implementation
272
273    this.commands.push(cmd);
274    cmd.parent = this;
275    return this;
276  }
277
278  /**
279   * Factory routine to create a new unattached argument.
280   *
281   * See .argument() for creating an attached argument, which uses this routine to
282   * create the argument. You can override createArgument to return a custom argument.
283   *
284   * @param {string} name
285   * @param {string} [description]
286   * @return {Argument} new argument
287   */
288
289  createArgument(name, description) {
290    return new Argument(name, description);
291  }
292
293  /**
294   * Define argument syntax for command.
295   *
296   * The default is that the argument is required, and you can explicitly
297   * indicate this with <> around the name. Put [] around the name for an optional argument.
298   *
299   * @example
300   * program.argument('<input-file>');
301   * program.argument('[output-file]');
302   *
303   * @param {string} name
304   * @param {string} [description]
305   * @param {Function|*} [fn] - custom argument processing function
306   * @param {*} [defaultValue]
307   * @return {Command} `this` command for chaining
308   */
309  argument(name, description, fn, defaultValue) {
310    const argument = this.createArgument(name, description);
311    if (typeof fn === 'function') {
312      argument.default(defaultValue).argParser(fn);
313    } else {
314      argument.default(fn);
315    }
316    this.addArgument(argument);
317    return this;
318  }
319
320  /**
321   * Define argument syntax for command, adding multiple at once (without descriptions).
322   *
323   * See also .argument().
324   *
325   * @example
326   * program.arguments('<cmd> [env]');
327   *
328   * @param {string} names
329   * @return {Command} `this` command for chaining
330   */
331
332  arguments(names) {
333    names.split(/ +/).forEach((detail) => {
334      this.argument(detail);
335    });
336    return this;
337  }
338
339  /**
340   * Define argument syntax for command, adding a prepared argument.
341   *
342   * @param {Argument} argument
343   * @return {Command} `this` command for chaining
344   */
345  addArgument(argument) {
346    const previousArgument = this._args.slice(-1)[0];
347    if (previousArgument && previousArgument.variadic) {
348      throw new Error(`only the last argument can be variadic '${previousArgument.name()}'`);
349    }
350    if (argument.required && argument.defaultValue !== undefined && argument.parseArg === undefined) {
351      throw new Error(`a default value for a required argument is never used: '${argument.name()}'`);
352    }
353    this._args.push(argument);
354    return this;
355  }
356
357  /**
358   * Override default decision whether to add implicit help command.
359   *
360   *    addHelpCommand() // force on
361   *    addHelpCommand(false); // force off
362   *    addHelpCommand('help [cmd]', 'display help for [cmd]'); // force on with custom details
363   *
364   * @return {Command} `this` command for chaining
365   */
366
367  addHelpCommand(enableOrNameAndArgs, description) {
368    if (enableOrNameAndArgs === false) {
369      this._addImplicitHelpCommand = false;
370    } else {
371      this._addImplicitHelpCommand = true;
372      if (typeof enableOrNameAndArgs === 'string') {
373        this._helpCommandName = enableOrNameAndArgs.split(' ')[0];
374        this._helpCommandnameAndArgs = enableOrNameAndArgs;
375      }
376      this._helpCommandDescription = description || this._helpCommandDescription;
377    }
378    return this;
379  }
380
381  /**
382   * @return {boolean}
383   * @api private
384   */
385
386  _hasImplicitHelpCommand() {
387    if (this._addImplicitHelpCommand === undefined) {
388      return this.commands.length && !this._actionHandler && !this._findCommand('help');
389    }
390    return this._addImplicitHelpCommand;
391  }
392
393  /**
394   * Add hook for life cycle event.
395   *
396   * @param {string} event
397   * @param {Function} listener
398   * @return {Command} `this` command for chaining
399   */
400
401  hook(event, listener) {
402    const allowedValues = ['preSubcommand', 'preAction', 'postAction'];
403    if (!allowedValues.includes(event)) {
404      throw new Error(`Unexpected value for event passed to hook : '${event}'.
405Expecting one of '${allowedValues.join("', '")}'`);
406    }
407    if (this._lifeCycleHooks[event]) {
408      this._lifeCycleHooks[event].push(listener);
409    } else {
410      this._lifeCycleHooks[event] = [listener];
411    }
412    return this;
413  }
414
415  /**
416   * Register callback to use as replacement for calling process.exit.
417   *
418   * @param {Function} [fn] optional callback which will be passed a CommanderError, defaults to throwing
419   * @return {Command} `this` command for chaining
420   */
421
422  exitOverride(fn) {
423    if (fn) {
424      this._exitCallback = fn;
425    } else {
426      this._exitCallback = (err) => {
427        if (err.code !== 'commander.executeSubCommandAsync') {
428          throw err;
429        } else {
430          // Async callback from spawn events, not useful to throw.
431        }
432      };
433    }
434    return this;
435  }
436
437  /**
438   * Call process.exit, and _exitCallback if defined.
439   *
440   * @param {number} exitCode exit code for using with process.exit
441   * @param {string} code an id string representing the error
442   * @param {string} message human-readable description of the error
443   * @return never
444   * @api private
445   */
446
447  _exit(exitCode, code, message) {
448    if (this._exitCallback) {
449      this._exitCallback(new CommanderError(exitCode, code, message));
450      // Expecting this line is not reached.
451    }
452    process.exit(exitCode);
453  }
454
455  /**
456   * Register callback `fn` for the command.
457   *
458   * @example
459   * program
460   *   .command('serve')
461   *   .description('start service')
462   *   .action(function() {
463   *      // do work here
464   *   });
465   *
466   * @param {Function} fn
467   * @return {Command} `this` command for chaining
468   */
469
470  action(fn) {
471    const listener = (args) => {
472      // The .action callback takes an extra parameter which is the command or options.
473      const expectedArgsCount = this._args.length;
474      const actionArgs = args.slice(0, expectedArgsCount);
475      if (this._storeOptionsAsProperties) {
476        actionArgs[expectedArgsCount] = this; // backwards compatible "options"
477      } else {
478        actionArgs[expectedArgsCount] = this.opts();
479      }
480      actionArgs.push(this);
481
482      return fn.apply(this, actionArgs);
483    };
484    this._actionHandler = listener;
485    return this;
486  }
487
488  /**
489   * Factory routine to create a new unattached option.
490   *
491   * See .option() for creating an attached option, which uses this routine to
492   * create the option. You can override createOption to return a custom option.
493   *
494   * @param {string} flags
495   * @param {string} [description]
496   * @return {Option} new option
497   */
498
499  createOption(flags, description) {
500    return new Option(flags, description);
501  }
502
503  /**
504   * Add an option.
505   *
506   * @param {Option} option
507   * @return {Command} `this` command for chaining
508   */
509  addOption(option) {
510    const oname = option.name();
511    const name = option.attributeName();
512
513    // store default value
514    if (option.negate) {
515      // --no-foo is special and defaults foo to true, unless a --foo option is already defined
516      const positiveLongFlag = option.long.replace(/^--no-/, '--');
517      if (!this._findOption(positiveLongFlag)) {
518        this.setOptionValueWithSource(name, option.defaultValue === undefined ? true : option.defaultValue, 'default');
519      }
520    } else if (option.defaultValue !== undefined) {
521      this.setOptionValueWithSource(name, option.defaultValue, 'default');
522    }
523
524    // register the option
525    this.options.push(option);
526
527    // handler for cli and env supplied values
528    const handleOptionValue = (val, invalidValueMessage, valueSource) => {
529      // val is null for optional option used without an optional-argument.
530      // val is undefined for boolean and negated option.
531      if (val == null && option.presetArg !== undefined) {
532        val = option.presetArg;
533      }
534
535      // custom processing
536      const oldValue = this.getOptionValue(name);
537      if (val !== null && option.parseArg) {
538        try {
539          val = option.parseArg(val, oldValue);
540        } catch (err) {
541          if (err.code === 'commander.invalidArgument') {
542            const message = `${invalidValueMessage} ${err.message}`;
543            this.error(message, { exitCode: err.exitCode, code: err.code });
544          }
545          throw err;
546        }
547      } else if (val !== null && option.variadic) {
548        val = option._concatValue(val, oldValue);
549      }
550
551      // Fill-in appropriate missing values. Long winded but easy to follow.
552      if (val == null) {
553        if (option.negate) {
554          val = false;
555        } else if (option.isBoolean() || option.optional) {
556          val = true;
557        } else {
558          val = ''; // not normal, parseArg might have failed or be a mock function for testing
559        }
560      }
561      this.setOptionValueWithSource(name, val, valueSource);
562    };
563
564    this.on('option:' + oname, (val) => {
565      const invalidValueMessage = `error: option '${option.flags}' argument '${val}' is invalid.`;
566      handleOptionValue(val, invalidValueMessage, 'cli');
567    });
568
569    if (option.envVar) {
570      this.on('optionEnv:' + oname, (val) => {
571        const invalidValueMessage = `error: option '${option.flags}' value '${val}' from env '${option.envVar}' is invalid.`;
572        handleOptionValue(val, invalidValueMessage, 'env');
573      });
574    }
575
576    return this;
577  }
578
579  /**
580   * Internal implementation shared by .option() and .requiredOption()
581   *
582   * @api private
583   */
584  _optionEx(config, flags, description, fn, defaultValue) {
585    if (typeof flags === 'object' && flags instanceof Option) {
586      throw new Error('To add an Option object use addOption() instead of option() or requiredOption()');
587    }
588    const option = this.createOption(flags, description);
589    option.makeOptionMandatory(!!config.mandatory);
590    if (typeof fn === 'function') {
591      option.default(defaultValue).argParser(fn);
592    } else if (fn instanceof RegExp) {
593      // deprecated
594      const regex = fn;
595      fn = (val, def) => {
596        const m = regex.exec(val);
597        return m ? m[0] : def;
598      };
599      option.default(defaultValue).argParser(fn);
600    } else {
601      option.default(fn);
602    }
603
604    return this.addOption(option);
605  }
606
607  /**
608   * Define option with `flags`, `description` and optional
609   * coercion `fn`.
610   *
611   * The `flags` string contains the short and/or long flags,
612   * separated by comma, a pipe or space. The following are all valid
613   * all will output this way when `--help` is used.
614   *
615   *     "-p, --pepper"
616   *     "-p|--pepper"
617   *     "-p --pepper"
618   *
619   * @example
620   * // simple boolean defaulting to undefined
621   * program.option('-p, --pepper', 'add pepper');
622   *
623   * program.pepper
624   * // => undefined
625   *
626   * --pepper
627   * program.pepper
628   * // => true
629   *
630   * // simple boolean defaulting to true (unless non-negated option is also defined)
631   * program.option('-C, --no-cheese', 'remove cheese');
632   *
633   * program.cheese
634   * // => true
635   *
636   * --no-cheese
637   * program.cheese
638   * // => false
639   *
640   * // required argument
641   * program.option('-C, --chdir <path>', 'change the working directory');
642   *
643   * --chdir /tmp
644   * program.chdir
645   * // => "/tmp"
646   *
647   * // optional argument
648   * program.option('-c, --cheese [type]', 'add cheese [marble]');
649   *
650   * @param {string} flags
651   * @param {string} [description]
652   * @param {Function|*} [fn] - custom option processing function or default value
653   * @param {*} [defaultValue]
654   * @return {Command} `this` command for chaining
655   */
656
657  option(flags, description, fn, defaultValue) {
658    return this._optionEx({}, flags, description, fn, defaultValue);
659  }
660
661  /**
662  * Add a required option which must have a value after parsing. This usually means
663  * the option must be specified on the command line. (Otherwise the same as .option().)
664  *
665  * The `flags` string contains the short and/or long flags, separated by comma, a pipe or space.
666  *
667  * @param {string} flags
668  * @param {string} [description]
669  * @param {Function|*} [fn] - custom option processing function or default value
670  * @param {*} [defaultValue]
671  * @return {Command} `this` command for chaining
672  */
673
674  requiredOption(flags, description, fn, defaultValue) {
675    return this._optionEx({ mandatory: true }, flags, description, fn, defaultValue);
676  }
677
678  /**
679   * Alter parsing of short flags with optional values.
680   *
681   * @example
682   * // for `.option('-f,--flag [value]'):
683   * program.combineFlagAndOptionalValue(true);  // `-f80` is treated like `--flag=80`, this is the default behaviour
684   * program.combineFlagAndOptionalValue(false) // `-fb` is treated like `-f -b`
685   *
686   * @param {Boolean} [combine=true] - if `true` or omitted, an optional value can be specified directly after the flag.
687   */
688  combineFlagAndOptionalValue(combine = true) {
689    this._combineFlagAndOptionalValue = !!combine;
690    return this;
691  }
692
693  /**
694   * Allow unknown options on the command line.
695   *
696   * @param {Boolean} [allowUnknown=true] - if `true` or omitted, no error will be thrown
697   * for unknown options.
698   */
699  allowUnknownOption(allowUnknown = true) {
700    this._allowUnknownOption = !!allowUnknown;
701    return this;
702  }
703
704  /**
705   * Allow excess command-arguments on the command line. Pass false to make excess arguments an error.
706   *
707   * @param {Boolean} [allowExcess=true] - if `true` or omitted, no error will be thrown
708   * for excess arguments.
709   */
710  allowExcessArguments(allowExcess = true) {
711    this._allowExcessArguments = !!allowExcess;
712    return this;
713  }
714
715  /**
716   * Enable positional options. Positional means global options are specified before subcommands which lets
717   * subcommands reuse the same option names, and also enables subcommands to turn on passThroughOptions.
718   * The default behaviour is non-positional and global options may appear anywhere on the command line.
719   *
720   * @param {Boolean} [positional=true]
721   */
722  enablePositionalOptions(positional = true) {
723    this._enablePositionalOptions = !!positional;
724    return this;
725  }
726
727  /**
728   * Pass through options that come after command-arguments rather than treat them as command-options,
729   * so actual command-options come before command-arguments. Turning this on for a subcommand requires
730   * positional options to have been enabled on the program (parent commands).
731   * The default behaviour is non-positional and options may appear before or after command-arguments.
732   *
733   * @param {Boolean} [passThrough=true]
734   * for unknown options.
735   */
736  passThroughOptions(passThrough = true) {
737    this._passThroughOptions = !!passThrough;
738    if (!!this.parent && passThrough && !this.parent._enablePositionalOptions) {
739      throw new Error('passThroughOptions can not be used without turning on enablePositionalOptions for parent command(s)');
740    }
741    return this;
742  }
743
744  /**
745    * Whether to store option values as properties on command object,
746    * or store separately (specify false). In both cases the option values can be accessed using .opts().
747    *
748    * @param {boolean} [storeAsProperties=true]
749    * @return {Command} `this` command for chaining
750    */
751
752  storeOptionsAsProperties(storeAsProperties = true) {
753    this._storeOptionsAsProperties = !!storeAsProperties;
754    if (this.options.length) {
755      throw new Error('call .storeOptionsAsProperties() before adding options');
756    }
757    return this;
758  }
759
760  /**
761   * Retrieve option value.
762   *
763   * @param {string} key
764   * @return {Object} value
765   */
766
767  getOptionValue(key) {
768    if (this._storeOptionsAsProperties) {
769      return this[key];
770    }
771    return this._optionValues[key];
772  }
773
774  /**
775   * Store option value.
776   *
777   * @param {string} key
778   * @param {Object} value
779   * @return {Command} `this` command for chaining
780   */
781
782  setOptionValue(key, value) {
783    return this.setOptionValueWithSource(key, value, undefined);
784  }
785
786  /**
787    * Store option value and where the value came from.
788    *
789    * @param {string} key
790    * @param {Object} value
791    * @param {string} source - expected values are default/config/env/cli/implied
792    * @return {Command} `this` command for chaining
793    */
794
795  setOptionValueWithSource(key, value, source) {
796    if (this._storeOptionsAsProperties) {
797      this[key] = value;
798    } else {
799      this._optionValues[key] = value;
800    }
801    this._optionValueSources[key] = source;
802    return this;
803  }
804
805  /**
806    * Get source of option value.
807    * Expected values are default | config | env | cli | implied
808    *
809    * @param {string} key
810    * @return {string}
811    */
812
813  getOptionValueSource(key) {
814    return this._optionValueSources[key];
815  }
816
817  /**
818    * Get source of option value. See also .optsWithGlobals().
819    * Expected values are default | config | env | cli | implied
820    *
821    * @param {string} key
822    * @return {string}
823    */
824
825  getOptionValueSourceWithGlobals(key) {
826    // global overwrites local, like optsWithGlobals
827    let source;
828    getCommandAndParents(this).forEach((cmd) => {
829      if (cmd.getOptionValueSource(key) !== undefined) {
830        source = cmd.getOptionValueSource(key);
831      }
832    });
833    return source;
834  }
835
836  /**
837   * Get user arguments from implied or explicit arguments.
838   * Side-effects: set _scriptPath if args included script. Used for default program name, and subcommand searches.
839   *
840   * @api private
841   */
842
843  _prepareUserArgs(argv, parseOptions) {
844    if (argv !== undefined && !Array.isArray(argv)) {
845      throw new Error('first parameter to parse must be array or undefined');
846    }
847    parseOptions = parseOptions || {};
848
849    // Default to using process.argv
850    if (argv === undefined) {
851      argv = process.argv;
852      // @ts-ignore: unknown property
853      if (process.versions && process.versions.electron) {
854        parseOptions.from = 'electron';
855      }
856    }
857    this.rawArgs = argv.slice();
858
859    // make it a little easier for callers by supporting various argv conventions
860    let userArgs;
861    switch (parseOptions.from) {
862      case undefined:
863      case 'node':
864        this._scriptPath = argv[1];
865        userArgs = argv.slice(2);
866        break;
867      case 'electron':
868        // @ts-ignore: unknown property
869        if (process.defaultApp) {
870          this._scriptPath = argv[1];
871          userArgs = argv.slice(2);
872        } else {
873          userArgs = argv.slice(1);
874        }
875        break;
876      case 'user':
877        userArgs = argv.slice(0);
878        break;
879      default:
880        throw new Error(`unexpected parse option { from: '${parseOptions.from}' }`);
881    }
882
883    // Find default name for program from arguments.
884    if (!this._name && this._scriptPath) this.nameFromFilename(this._scriptPath);
885    this._name = this._name || 'program';
886
887    return userArgs;
888  }
889
890  /**
891   * Parse `argv`, setting options and invoking commands when defined.
892   *
893   * The default expectation is that the arguments are from node and have the application as argv[0]
894   * and the script being run in argv[1], with user parameters after that.
895   *
896   * @example
897   * program.parse(process.argv);
898   * program.parse(); // implicitly use process.argv and auto-detect node vs electron conventions
899   * program.parse(my-args, { from: 'user' }); // just user supplied arguments, nothing special about argv[0]
900   *
901   * @param {string[]} [argv] - optional, defaults to process.argv
902   * @param {Object} [parseOptions] - optionally specify style of options with from: node/user/electron
903   * @param {string} [parseOptions.from] - where the args are from: 'node', 'user', 'electron'
904   * @return {Command} `this` command for chaining
905   */
906
907  parse(argv, parseOptions) {
908    const userArgs = this._prepareUserArgs(argv, parseOptions);
909    this._parseCommand([], userArgs);
910
911    return this;
912  }
913
914  /**
915   * Parse `argv`, setting options and invoking commands when defined.
916   *
917   * Use parseAsync instead of parse if any of your action handlers are async. Returns a Promise.
918   *
919   * The default expectation is that the arguments are from node and have the application as argv[0]
920   * and the script being run in argv[1], with user parameters after that.
921   *
922   * @example
923   * await program.parseAsync(process.argv);
924   * await program.parseAsync(); // implicitly use process.argv and auto-detect node vs electron conventions
925   * await program.parseAsync(my-args, { from: 'user' }); // just user supplied arguments, nothing special about argv[0]
926   *
927   * @param {string[]} [argv]
928   * @param {Object} [parseOptions]
929   * @param {string} parseOptions.from - where the args are from: 'node', 'user', 'electron'
930   * @return {Promise}
931   */
932
933  async parseAsync(argv, parseOptions) {
934    const userArgs = this._prepareUserArgs(argv, parseOptions);
935    await this._parseCommand([], userArgs);
936
937    return this;
938  }
939
940  /**
941   * Execute a sub-command executable.
942   *
943   * @api private
944   */
945
946  _executeSubCommand(subcommand, args) {
947    args = args.slice();
948    let launchWithNode = false; // Use node for source targets so do not need to get permissions correct, and on Windows.
949    const sourceExt = ['.js', '.ts', '.tsx', '.mjs', '.cjs'];
950
951    function findFile(baseDir, baseName) {
952      // Look for specified file
953      const localBin = path.resolve(baseDir, baseName);
954      if (fs.existsSync(localBin)) return localBin;
955
956      // Stop looking if candidate already has an expected extension.
957      if (sourceExt.includes(path.extname(baseName))) return undefined;
958
959      // Try all the extensions.
960      const foundExt = sourceExt.find(ext => fs.existsSync(`${localBin}${ext}`));
961      if (foundExt) return `${localBin}${foundExt}`;
962
963      return undefined;
964    }
965
966    // Not checking for help first. Unlikely to have mandatory and executable, and can't robustly test for help flags in external command.
967    this._checkForMissingMandatoryOptions();
968    this._checkForConflictingOptions();
969
970    // executableFile and executableDir might be full path, or just a name
971    let executableFile = subcommand._executableFile || `${this._name}-${subcommand._name}`;
972    let executableDir = this._executableDir || '';
973    if (this._scriptPath) {
974      let resolvedScriptPath; // resolve possible symlink for installed npm binary
975      try {
976        resolvedScriptPath = fs.realpathSync(this._scriptPath);
977      } catch (err) {
978        resolvedScriptPath = this._scriptPath;
979      }
980      executableDir = path.resolve(path.dirname(resolvedScriptPath), executableDir);
981    }
982
983    // Look for a local file in preference to a command in PATH.
984    if (executableDir) {
985      let localFile = findFile(executableDir, executableFile);
986
987      // Legacy search using prefix of script name instead of command name
988      if (!localFile && !subcommand._executableFile && this._scriptPath) {
989        const legacyName = path.basename(this._scriptPath, path.extname(this._scriptPath));
990        if (legacyName !== this._name) {
991          localFile = findFile(executableDir, `${legacyName}-${subcommand._name}`);
992        }
993      }
994      executableFile = localFile || executableFile;
995    }
996
997    launchWithNode = sourceExt.includes(path.extname(executableFile));
998
999    let proc;
1000    if (process.platform !== 'win32') {
1001      if (launchWithNode) {
1002        args.unshift(executableFile);
1003        // add executable arguments to spawn
1004        args = incrementNodeInspectorPort(process.execArgv).concat(args);
1005
1006        proc = childProcess.spawn(process.argv[0], args, { stdio: 'inherit' });
1007      } else {
1008        proc = childProcess.spawn(executableFile, args, { stdio: 'inherit' });
1009      }
1010    } else {
1011      args.unshift(executableFile);
1012      // add executable arguments to spawn
1013      args = incrementNodeInspectorPort(process.execArgv).concat(args);
1014      proc = childProcess.spawn(process.execPath, args, { stdio: 'inherit' });
1015    }
1016
1017    if (!proc.killed) { // testing mainly to avoid leak warnings during unit tests with mocked spawn
1018      const signals = ['SIGUSR1', 'SIGUSR2', 'SIGTERM', 'SIGINT', 'SIGHUP'];
1019      signals.forEach((signal) => {
1020        // @ts-ignore
1021        process.on(signal, () => {
1022          if (proc.killed === false && proc.exitCode === null) {
1023            proc.kill(signal);
1024          }
1025        });
1026      });
1027    }
1028
1029    // By default terminate process when spawned process terminates.
1030    // Suppressing the exit if exitCallback defined is a bit messy and of limited use, but does allow process to stay running!
1031    const exitCallback = this._exitCallback;
1032    if (!exitCallback) {
1033      proc.on('close', process.exit.bind(process));
1034    } else {
1035      proc.on('close', () => {
1036        exitCallback(new CommanderError(process.exitCode || 0, 'commander.executeSubCommandAsync', '(close)'));
1037      });
1038    }
1039    proc.on('error', (err) => {
1040      // @ts-ignore
1041      if (err.code === 'ENOENT') {
1042        const executableDirMessage = executableDir
1043          ? `searched for local subcommand relative to directory '${executableDir}'`
1044          : 'no directory for search for local subcommand, use .executableDir() to supply a custom directory';
1045        const executableMissing = `'${executableFile}' does not exist
1046 - if '${subcommand._name}' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead
1047 - if the default executable name is not suitable, use the executableFile option to supply a custom name or path
1048 - ${executableDirMessage}`;
1049        throw new Error(executableMissing);
1050      // @ts-ignore
1051      } else if (err.code === 'EACCES') {
1052        throw new Error(`'${executableFile}' not executable`);
1053      }
1054      if (!exitCallback) {
1055        process.exit(1);
1056      } else {
1057        const wrappedError = new CommanderError(1, 'commander.executeSubCommandAsync', '(error)');
1058        wrappedError.nestedError = err;
1059        exitCallback(wrappedError);
1060      }
1061    });
1062
1063    // Store the reference to the child process
1064    this.runningCommand = proc;
1065  }
1066
1067  /**
1068   * @api private
1069   */
1070
1071  _dispatchSubcommand(commandName, operands, unknown) {
1072    const subCommand = this._findCommand(commandName);
1073    if (!subCommand) this.help({ error: true });
1074
1075    let hookResult;
1076    hookResult = this._chainOrCallSubCommandHook(hookResult, subCommand, 'preSubcommand');
1077    hookResult = this._chainOrCall(hookResult, () => {
1078      if (subCommand._executableHandler) {
1079        this._executeSubCommand(subCommand, operands.concat(unknown));
1080      } else {
1081        return subCommand._parseCommand(operands, unknown);
1082      }
1083    });
1084    return hookResult;
1085  }
1086
1087  /**
1088   * Check this.args against expected this._args.
1089   *
1090   * @api private
1091   */
1092
1093  _checkNumberOfArguments() {
1094    // too few
1095    this._args.forEach((arg, i) => {
1096      if (arg.required && this.args[i] == null) {
1097        this.missingArgument(arg.name());
1098      }
1099    });
1100    // too many
1101    if (this._args.length > 0 && this._args[this._args.length - 1].variadic) {
1102      return;
1103    }
1104    if (this.args.length > this._args.length) {
1105      this._excessArguments(this.args);
1106    }
1107  }
1108
1109  /**
1110   * Process this.args using this._args and save as this.processedArgs!
1111   *
1112   * @api private
1113   */
1114
1115  _processArguments() {
1116    const myParseArg = (argument, value, previous) => {
1117      // Extra processing for nice error message on parsing failure.
1118      let parsedValue = value;
1119      if (value !== null && argument.parseArg) {
1120        try {
1121          parsedValue = argument.parseArg(value, previous);
1122        } catch (err) {
1123          if (err.code === 'commander.invalidArgument') {
1124            const message = `error: command-argument value '${value}' is invalid for argument '${argument.name()}'. ${err.message}`;
1125            this.error(message, { exitCode: err.exitCode, code: err.code });
1126          }
1127          throw err;
1128        }
1129      }
1130      return parsedValue;
1131    };
1132
1133    this._checkNumberOfArguments();
1134
1135    const processedArgs = [];
1136    this._args.forEach((declaredArg, index) => {
1137      let value = declaredArg.defaultValue;
1138      if (declaredArg.variadic) {
1139        // Collect together remaining arguments for passing together as an array.
1140        if (index < this.args.length) {
1141          value = this.args.slice(index);
1142          if (declaredArg.parseArg) {
1143            value = value.reduce((processed, v) => {
1144              return myParseArg(declaredArg, v, processed);
1145            }, declaredArg.defaultValue);
1146          }
1147        } else if (value === undefined) {
1148          value = [];
1149        }
1150      } else if (index < this.args.length) {
1151        value = this.args[index];
1152        if (declaredArg.parseArg) {
1153          value = myParseArg(declaredArg, value, declaredArg.defaultValue);
1154        }
1155      }
1156      processedArgs[index] = value;
1157    });
1158    this.processedArgs = processedArgs;
1159  }
1160
1161  /**
1162   * Once we have a promise we chain, but call synchronously until then.
1163   *
1164   * @param {Promise|undefined} promise
1165   * @param {Function} fn
1166   * @return {Promise|undefined}
1167   * @api private
1168   */
1169
1170  _chainOrCall(promise, fn) {
1171    // thenable
1172    if (promise && promise.then && typeof promise.then === 'function') {
1173      // already have a promise, chain callback
1174      return promise.then(() => fn());
1175    }
1176    // callback might return a promise
1177    return fn();
1178  }
1179
1180  /**
1181   *
1182   * @param {Promise|undefined} promise
1183   * @param {string} event
1184   * @return {Promise|undefined}
1185   * @api private
1186   */
1187
1188  _chainOrCallHooks(promise, event) {
1189    let result = promise;
1190    const hooks = [];
1191    getCommandAndParents(this)
1192      .reverse()
1193      .filter(cmd => cmd._lifeCycleHooks[event] !== undefined)
1194      .forEach(hookedCommand => {
1195        hookedCommand._lifeCycleHooks[event].forEach((callback) => {
1196          hooks.push({ hookedCommand, callback });
1197        });
1198      });
1199    if (event === 'postAction') {
1200      hooks.reverse();
1201    }
1202
1203    hooks.forEach((hookDetail) => {
1204      result = this._chainOrCall(result, () => {
1205        return hookDetail.callback(hookDetail.hookedCommand, this);
1206      });
1207    });
1208    return result;
1209  }
1210
1211  /**
1212   *
1213   * @param {Promise|undefined} promise
1214   * @param {Command} subCommand
1215   * @param {string} event
1216   * @return {Promise|undefined}
1217   * @api private
1218   */
1219
1220  _chainOrCallSubCommandHook(promise, subCommand, event) {
1221    let result = promise;
1222    if (this._lifeCycleHooks[event] !== undefined) {
1223      this._lifeCycleHooks[event].forEach((hook) => {
1224        result = this._chainOrCall(result, () => {
1225          return hook(this, subCommand);
1226        });
1227      });
1228    }
1229    return result;
1230  }
1231
1232  /**
1233   * Process arguments in context of this command.
1234   * Returns action result, in case it is a promise.
1235   *
1236   * @api private
1237   */
1238
1239  _parseCommand(operands, unknown) {
1240    const parsed = this.parseOptions(unknown);
1241    this._parseOptionsEnv(); // after cli, so parseArg not called on both cli and env
1242    this._parseOptionsImplied();
1243    operands = operands.concat(parsed.operands);
1244    unknown = parsed.unknown;
1245    this.args = operands.concat(unknown);
1246
1247    if (operands && this._findCommand(operands[0])) {
1248      return this._dispatchSubcommand(operands[0], operands.slice(1), unknown);
1249    }
1250    if (this._hasImplicitHelpCommand() && operands[0] === this._helpCommandName) {
1251      if (operands.length === 1) {
1252        this.help();
1253      }
1254      return this._dispatchSubcommand(operands[1], [], [this._helpLongFlag]);
1255    }
1256    if (this._defaultCommandName) {
1257      outputHelpIfRequested(this, unknown); // Run the help for default command from parent rather than passing to default command
1258      return this._dispatchSubcommand(this._defaultCommandName, operands, unknown);
1259    }
1260    if (this.commands.length && this.args.length === 0 && !this._actionHandler && !this._defaultCommandName) {
1261      // probably missing subcommand and no handler, user needs help (and exit)
1262      this.help({ error: true });
1263    }
1264
1265    outputHelpIfRequested(this, parsed.unknown);
1266    this._checkForMissingMandatoryOptions();
1267    this._checkForConflictingOptions();
1268
1269    // We do not always call this check to avoid masking a "better" error, like unknown command.
1270    const checkForUnknownOptions = () => {
1271      if (parsed.unknown.length > 0) {
1272        this.unknownOption(parsed.unknown[0]);
1273      }
1274    };
1275
1276    const commandEvent = `command:${this.name()}`;
1277    if (this._actionHandler) {
1278      checkForUnknownOptions();
1279      this._processArguments();
1280
1281      let actionResult;
1282      actionResult = this._chainOrCallHooks(actionResult, 'preAction');
1283      actionResult = this._chainOrCall(actionResult, () => this._actionHandler(this.processedArgs));
1284      if (this.parent) {
1285        actionResult = this._chainOrCall(actionResult, () => {
1286          this.parent.emit(commandEvent, operands, unknown); // legacy
1287        });
1288      }
1289      actionResult = this._chainOrCallHooks(actionResult, 'postAction');
1290      return actionResult;
1291    }
1292    if (this.parent && this.parent.listenerCount(commandEvent)) {
1293      checkForUnknownOptions();
1294      this._processArguments();
1295      this.parent.emit(commandEvent, operands, unknown); // legacy
1296    } else if (operands.length) {
1297      if (this._findCommand('*')) { // legacy default command
1298        return this._dispatchSubcommand('*', operands, unknown);
1299      }
1300      if (this.listenerCount('command:*')) {
1301        // skip option check, emit event for possible misspelling suggestion
1302        this.emit('command:*', operands, unknown);
1303      } else if (this.commands.length) {
1304        this.unknownCommand();
1305      } else {
1306        checkForUnknownOptions();
1307        this._processArguments();
1308      }
1309    } else if (this.commands.length) {
1310      checkForUnknownOptions();
1311      // This command has subcommands and nothing hooked up at this level, so display help (and exit).
1312      this.help({ error: true });
1313    } else {
1314      checkForUnknownOptions();
1315      this._processArguments();
1316      // fall through for caller to handle after calling .parse()
1317    }
1318  }
1319
1320  /**
1321   * Find matching command.
1322   *
1323   * @api private
1324   */
1325  _findCommand(name) {
1326    if (!name) return undefined;
1327    return this.commands.find(cmd => cmd._name === name || cmd._aliases.includes(name));
1328  }
1329
1330  /**
1331   * Return an option matching `arg` if any.
1332   *
1333   * @param {string} arg
1334   * @return {Option}
1335   * @api private
1336   */
1337
1338  _findOption(arg) {
1339    return this.options.find(option => option.is(arg));
1340  }
1341
1342  /**
1343   * Display an error message if a mandatory option does not have a value.
1344   * Called after checking for help flags in leaf subcommand.
1345   *
1346   * @api private
1347   */
1348
1349  _checkForMissingMandatoryOptions() {
1350    // Walk up hierarchy so can call in subcommand after checking for displaying help.
1351    for (let cmd = this; cmd; cmd = cmd.parent) {
1352      cmd.options.forEach((anOption) => {
1353        if (anOption.mandatory && (cmd.getOptionValue(anOption.attributeName()) === undefined)) {
1354          cmd.missingMandatoryOptionValue(anOption);
1355        }
1356      });
1357    }
1358  }
1359
1360  /**
1361   * Display an error message if conflicting options are used together in this.
1362   *
1363   * @api private
1364   */
1365  _checkForConflictingLocalOptions() {
1366    const definedNonDefaultOptions = this.options.filter(
1367      (option) => {
1368        const optionKey = option.attributeName();
1369        if (this.getOptionValue(optionKey) === undefined) {
1370          return false;
1371        }
1372        return this.getOptionValueSource(optionKey) !== 'default';
1373      }
1374    );
1375
1376    const optionsWithConflicting = definedNonDefaultOptions.filter(
1377      (option) => option.conflictsWith.length > 0
1378    );
1379
1380    optionsWithConflicting.forEach((option) => {
1381      const conflictingAndDefined = definedNonDefaultOptions.find((defined) =>
1382        option.conflictsWith.includes(defined.attributeName())
1383      );
1384      if (conflictingAndDefined) {
1385        this._conflictingOption(option, conflictingAndDefined);
1386      }
1387    });
1388  }
1389
1390  /**
1391   * Display an error message if conflicting options are used together.
1392   * Called after checking for help flags in leaf subcommand.
1393   *
1394   * @api private
1395   */
1396  _checkForConflictingOptions() {
1397    // Walk up hierarchy so can call in subcommand after checking for displaying help.
1398    for (let cmd = this; cmd; cmd = cmd.parent) {
1399      cmd._checkForConflictingLocalOptions();
1400    }
1401  }
1402
1403  /**
1404   * Parse options from `argv` removing known options,
1405   * and return argv split into operands and unknown arguments.
1406   *
1407   * Examples:
1408   *
1409   *     argv => operands, unknown
1410   *     --known kkk op => [op], []
1411   *     op --known kkk => [op], []
1412   *     sub --unknown uuu op => [sub], [--unknown uuu op]
1413   *     sub -- --unknown uuu op => [sub --unknown uuu op], []
1414   *
1415   * @param {String[]} argv
1416   * @return {{operands: String[], unknown: String[]}}
1417   */
1418
1419  parseOptions(argv) {
1420    const operands = []; // operands, not options or values
1421    const unknown = []; // first unknown option and remaining unknown args
1422    let dest = operands;
1423    const args = argv.slice();
1424
1425    function maybeOption(arg) {
1426      return arg.length > 1 && arg[0] === '-';
1427    }
1428
1429    // parse options
1430    let activeVariadicOption = null;
1431    while (args.length) {
1432      const arg = args.shift();
1433
1434      // literal
1435      if (arg === '--') {
1436        if (dest === unknown) dest.push(arg);
1437        dest.push(...args);
1438        break;
1439      }
1440
1441      if (activeVariadicOption && !maybeOption(arg)) {
1442        this.emit(`option:${activeVariadicOption.name()}`, arg);
1443        continue;
1444      }
1445      activeVariadicOption = null;
1446
1447      if (maybeOption(arg)) {
1448        const option = this._findOption(arg);
1449        // recognised option, call listener to assign value with possible custom processing
1450        if (option) {
1451          if (option.required) {
1452            const value = args.shift();
1453            if (value === undefined) this.optionMissingArgument(option);
1454            this.emit(`option:${option.name()}`, value);
1455          } else if (option.optional) {
1456            let value = null;
1457            // historical behaviour is optional value is following arg unless an option
1458            if (args.length > 0 && !maybeOption(args[0])) {
1459              value = args.shift();
1460            }
1461            this.emit(`option:${option.name()}`, value);
1462          } else { // boolean flag
1463            this.emit(`option:${option.name()}`);
1464          }
1465          activeVariadicOption = option.variadic ? option : null;
1466          continue;
1467        }
1468      }
1469
1470      // Look for combo options following single dash, eat first one if known.
1471      if (arg.length > 2 && arg[0] === '-' && arg[1] !== '-') {
1472        const option = this._findOption(`-${arg[1]}`);
1473        if (option) {
1474          if (option.required || (option.optional && this._combineFlagAndOptionalValue)) {
1475            // option with value following in same argument
1476            this.emit(`option:${option.name()}`, arg.slice(2));
1477          } else {
1478            // boolean option, emit and put back remainder of arg for further processing
1479            this.emit(`option:${option.name()}`);
1480            args.unshift(`-${arg.slice(2)}`);
1481          }
1482          continue;
1483        }
1484      }
1485
1486      // Look for known long flag with value, like --foo=bar
1487      if (/^--[^=]+=/.test(arg)) {
1488        const index = arg.indexOf('=');
1489        const option = this._findOption(arg.slice(0, index));
1490        if (option && (option.required || option.optional)) {
1491          this.emit(`option:${option.name()}`, arg.slice(index + 1));
1492          continue;
1493        }
1494      }
1495
1496      // Not a recognised option by this command.
1497      // Might be a command-argument, or subcommand option, or unknown option, or help command or option.
1498
1499      // An unknown option means further arguments also classified as unknown so can be reprocessed by subcommands.
1500      if (maybeOption(arg)) {
1501        dest = unknown;
1502      }
1503
1504      // If using positionalOptions, stop processing our options at subcommand.
1505      if ((this._enablePositionalOptions || this._passThroughOptions) && operands.length === 0 && unknown.length === 0) {
1506        if (this._findCommand(arg)) {
1507          operands.push(arg);
1508          if (args.length > 0) unknown.push(...args);
1509          break;
1510        } else if (arg === this._helpCommandName && this._hasImplicitHelpCommand()) {
1511          operands.push(arg);
1512          if (args.length > 0) operands.push(...args);
1513          break;
1514        } else if (this._defaultCommandName) {
1515          unknown.push(arg);
1516          if (args.length > 0) unknown.push(...args);
1517          break;
1518        }
1519      }
1520
1521      // If using passThroughOptions, stop processing options at first command-argument.
1522      if (this._passThroughOptions) {
1523        dest.push(arg);
1524        if (args.length > 0) dest.push(...args);
1525        break;
1526      }
1527
1528      // add arg
1529      dest.push(arg);
1530    }
1531
1532    return { operands, unknown };
1533  }
1534
1535  /**
1536   * Return an object containing local option values as key-value pairs.
1537   *
1538   * @return {Object}
1539   */
1540  opts() {
1541    if (this._storeOptionsAsProperties) {
1542      // Preserve original behaviour so backwards compatible when still using properties
1543      const result = {};
1544      const len = this.options.length;
1545
1546      for (let i = 0; i < len; i++) {
1547        const key = this.options[i].attributeName();
1548        result[key] = key === this._versionOptionName ? this._version : this[key];
1549      }
1550      return result;
1551    }
1552
1553    return this._optionValues;
1554  }
1555
1556  /**
1557   * Return an object containing merged local and global option values as key-value pairs.
1558   *
1559   * @return {Object}
1560   */
1561  optsWithGlobals() {
1562    // globals overwrite locals
1563    return getCommandAndParents(this).reduce(
1564      (combinedOptions, cmd) => Object.assign(combinedOptions, cmd.opts()),
1565      {}
1566    );
1567  }
1568
1569  /**
1570   * Display error message and exit (or call exitOverride).
1571   *
1572   * @param {string} message
1573   * @param {Object} [errorOptions]
1574   * @param {string} [errorOptions.code] - an id string representing the error
1575   * @param {number} [errorOptions.exitCode] - used with process.exit
1576   */
1577  error(message, errorOptions) {
1578    // output handling
1579    this._outputConfiguration.outputError(`${message}\n`, this._outputConfiguration.writeErr);
1580    if (typeof this._showHelpAfterError === 'string') {
1581      this._outputConfiguration.writeErr(`${this._showHelpAfterError}\n`);
1582    } else if (this._showHelpAfterError) {
1583      this._outputConfiguration.writeErr('\n');
1584      this.outputHelp({ error: true });
1585    }
1586
1587    // exit handling
1588    const config = errorOptions || {};
1589    const exitCode = config.exitCode || 1;
1590    const code = config.code || 'commander.error';
1591    this._exit(exitCode, code, message);
1592  }
1593
1594  /**
1595   * Apply any option related environment variables, if option does
1596   * not have a value from cli or client code.
1597   *
1598   * @api private
1599   */
1600  _parseOptionsEnv() {
1601    this.options.forEach((option) => {
1602      if (option.envVar && option.envVar in process.env) {
1603        const optionKey = option.attributeName();
1604        // Priority check. Do not overwrite cli or options from unknown source (client-code).
1605        if (this.getOptionValue(optionKey) === undefined || ['default', 'config', 'env'].includes(this.getOptionValueSource(optionKey))) {
1606          if (option.required || option.optional) { // option can take a value
1607            // keep very simple, optional always takes value
1608            this.emit(`optionEnv:${option.name()}`, process.env[option.envVar]);
1609          } else { // boolean
1610            // keep very simple, only care that envVar defined and not the value
1611            this.emit(`optionEnv:${option.name()}`);
1612          }
1613        }
1614      }
1615    });
1616  }
1617
1618  /**
1619   * Apply any implied option values, if option is undefined or default value.
1620   *
1621   * @api private
1622   */
1623  _parseOptionsImplied() {
1624    const dualHelper = new DualOptions(this.options);
1625    const hasCustomOptionValue = (optionKey) => {
1626      return this.getOptionValue(optionKey) !== undefined && !['default', 'implied'].includes(this.getOptionValueSource(optionKey));
1627    };
1628    this.options
1629      .filter(option => (option.implied !== undefined) &&
1630        hasCustomOptionValue(option.attributeName()) &&
1631        dualHelper.valueFromOption(this.getOptionValue(option.attributeName()), option))
1632      .forEach((option) => {
1633        Object.keys(option.implied)
1634          .filter(impliedKey => !hasCustomOptionValue(impliedKey))
1635          .forEach(impliedKey => {
1636            this.setOptionValueWithSource(impliedKey, option.implied[impliedKey], 'implied');
1637          });
1638      });
1639  }
1640
1641  /**
1642   * Argument `name` is missing.
1643   *
1644   * @param {string} name
1645   * @api private
1646   */
1647
1648  missingArgument(name) {
1649    const message = `error: missing required argument '${name}'`;
1650    this.error(message, { code: 'commander.missingArgument' });
1651  }
1652
1653  /**
1654   * `Option` is missing an argument.
1655   *
1656   * @param {Option} option
1657   * @api private
1658   */
1659
1660  optionMissingArgument(option) {
1661    const message = `error: option '${option.flags}' argument missing`;
1662    this.error(message, { code: 'commander.optionMissingArgument' });
1663  }
1664
1665  /**
1666   * `Option` does not have a value, and is a mandatory option.
1667   *
1668   * @param {Option} option
1669   * @api private
1670   */
1671
1672  missingMandatoryOptionValue(option) {
1673    const message = `error: required option '${option.flags}' not specified`;
1674    this.error(message, { code: 'commander.missingMandatoryOptionValue' });
1675  }
1676
1677  /**
1678   * `Option` conflicts with another option.
1679   *
1680   * @param {Option} option
1681   * @param {Option} conflictingOption
1682   * @api private
1683   */
1684  _conflictingOption(option, conflictingOption) {
1685    // The calling code does not know whether a negated option is the source of the
1686    // value, so do some work to take an educated guess.
1687    const findBestOptionFromValue = (option) => {
1688      const optionKey = option.attributeName();
1689      const optionValue = this.getOptionValue(optionKey);
1690      const negativeOption = this.options.find(target => target.negate && optionKey === target.attributeName());
1691      const positiveOption = this.options.find(target => !target.negate && optionKey === target.attributeName());
1692      if (negativeOption && (
1693        (negativeOption.presetArg === undefined && optionValue === false) ||
1694        (negativeOption.presetArg !== undefined && optionValue === negativeOption.presetArg)
1695      )) {
1696        return negativeOption;
1697      }
1698      return positiveOption || option;
1699    };
1700
1701    const getErrorMessage = (option) => {
1702      const bestOption = findBestOptionFromValue(option);
1703      const optionKey = bestOption.attributeName();
1704      const source = this.getOptionValueSource(optionKey);
1705      if (source === 'env') {
1706        return `environment variable '${bestOption.envVar}'`;
1707      }
1708      return `option '${bestOption.flags}'`;
1709    };
1710
1711    const message = `error: ${getErrorMessage(option)} cannot be used with ${getErrorMessage(conflictingOption)}`;
1712    this.error(message, { code: 'commander.conflictingOption' });
1713  }
1714
1715  /**
1716   * Unknown option `flag`.
1717   *
1718   * @param {string} flag
1719   * @api private
1720   */
1721
1722  unknownOption(flag) {
1723    if (this._allowUnknownOption) return;
1724    let suggestion = '';
1725
1726    if (flag.startsWith('--') && this._showSuggestionAfterError) {
1727      // Looping to pick up the global options too
1728      let candidateFlags = [];
1729      let command = this;
1730      do {
1731        const moreFlags = command.createHelp().visibleOptions(command)
1732          .filter(option => option.long)
1733          .map(option => option.long);
1734        candidateFlags = candidateFlags.concat(moreFlags);
1735        command = command.parent;
1736      } while (command && !command._enablePositionalOptions);
1737      suggestion = suggestSimilar(flag, candidateFlags);
1738    }
1739
1740    const message = `error: unknown option '${flag}'${suggestion}`;
1741    this.error(message, { code: 'commander.unknownOption' });
1742  }
1743
1744  /**
1745   * Excess arguments, more than expected.
1746   *
1747   * @param {string[]} receivedArgs
1748   * @api private
1749   */
1750
1751  _excessArguments(receivedArgs) {
1752    if (this._allowExcessArguments) return;
1753
1754    const expected = this._args.length;
1755    const s = (expected === 1) ? '' : 's';
1756    const forSubcommand = this.parent ? ` for '${this.name()}'` : '';
1757    const message = `error: too many arguments${forSubcommand}. Expected ${expected} argument${s} but got ${receivedArgs.length}.`;
1758    this.error(message, { code: 'commander.excessArguments' });
1759  }
1760
1761  /**
1762   * Unknown command.
1763   *
1764   * @api private
1765   */
1766
1767  unknownCommand() {
1768    const unknownName = this.args[0];
1769    let suggestion = '';
1770
1771    if (this._showSuggestionAfterError) {
1772      const candidateNames = [];
1773      this.createHelp().visibleCommands(this).forEach((command) => {
1774        candidateNames.push(command.name());
1775        // just visible alias
1776        if (command.alias()) candidateNames.push(command.alias());
1777      });
1778      suggestion = suggestSimilar(unknownName, candidateNames);
1779    }
1780
1781    const message = `error: unknown command '${unknownName}'${suggestion}`;
1782    this.error(message, { code: 'commander.unknownCommand' });
1783  }
1784
1785  /**
1786   * Set the program version to `str`.
1787   *
1788   * This method auto-registers the "-V, --version" flag
1789   * which will print the version number when passed.
1790   *
1791   * You can optionally supply the  flags and description to override the defaults.
1792   *
1793   * @param {string} str
1794   * @param {string} [flags]
1795   * @param {string} [description]
1796   * @return {this | string} `this` command for chaining, or version string if no arguments
1797   */
1798
1799  version(str, flags, description) {
1800    if (str === undefined) return this._version;
1801    this._version = str;
1802    flags = flags || '-V, --version';
1803    description = description || 'output the version number';
1804    const versionOption = this.createOption(flags, description);
1805    this._versionOptionName = versionOption.attributeName();
1806    this.options.push(versionOption);
1807    this.on('option:' + versionOption.name(), () => {
1808      this._outputConfiguration.writeOut(`${str}\n`);
1809      this._exit(0, 'commander.version', str);
1810    });
1811    return this;
1812  }
1813
1814  /**
1815   * Set the description.
1816   *
1817   * @param {string} [str]
1818   * @param {Object} [argsDescription]
1819   * @return {string|Command}
1820   */
1821  description(str, argsDescription) {
1822    if (str === undefined && argsDescription === undefined) return this._description;
1823    this._description = str;
1824    if (argsDescription) {
1825      this._argsDescription = argsDescription;
1826    }
1827    return this;
1828  }
1829
1830  /**
1831   * Set the summary. Used when listed as subcommand of parent.
1832   *
1833   * @param {string} [str]
1834   * @return {string|Command}
1835   */
1836  summary(str) {
1837    if (str === undefined) return this._summary;
1838    this._summary = str;
1839    return this;
1840  }
1841
1842  /**
1843   * Set an alias for the command.
1844   *
1845   * You may call more than once to add multiple aliases. Only the first alias is shown in the auto-generated help.
1846   *
1847   * @param {string} [alias]
1848   * @return {string|Command}
1849   */
1850
1851  alias(alias) {
1852    if (alias === undefined) return this._aliases[0]; // just return first, for backwards compatibility
1853
1854    /** @type {Command} */
1855    let command = this;
1856    if (this.commands.length !== 0 && this.commands[this.commands.length - 1]._executableHandler) {
1857      // assume adding alias for last added executable subcommand, rather than this
1858      command = this.commands[this.commands.length - 1];
1859    }
1860
1861    if (alias === command._name) throw new Error('Command alias can\'t be the same as its name');
1862
1863    command._aliases.push(alias);
1864    return this;
1865  }
1866
1867  /**
1868   * Set aliases for the command.
1869   *
1870   * Only the first alias is shown in the auto-generated help.
1871   *
1872   * @param {string[]} [aliases]
1873   * @return {string[]|Command}
1874   */
1875
1876  aliases(aliases) {
1877    // Getter for the array of aliases is the main reason for having aliases() in addition to alias().
1878    if (aliases === undefined) return this._aliases;
1879
1880    aliases.forEach((alias) => this.alias(alias));
1881    return this;
1882  }
1883
1884  /**
1885   * Set / get the command usage `str`.
1886   *
1887   * @param {string} [str]
1888   * @return {String|Command}
1889   */
1890
1891  usage(str) {
1892    if (str === undefined) {
1893      if (this._usage) return this._usage;
1894
1895      const args = this._args.map((arg) => {
1896        return humanReadableArgName(arg);
1897      });
1898      return [].concat(
1899        (this.options.length || this._hasHelpOption ? '[options]' : []),
1900        (this.commands.length ? '[command]' : []),
1901        (this._args.length ? args : [])
1902      ).join(' ');
1903    }
1904
1905    this._usage = str;
1906    return this;
1907  }
1908
1909  /**
1910   * Get or set the name of the command.
1911   *
1912   * @param {string} [str]
1913   * @return {string|Command}
1914   */
1915
1916  name(str) {
1917    if (str === undefined) return this._name;
1918    this._name = str;
1919    return this;
1920  }
1921
1922  /**
1923   * Set the name of the command from script filename, such as process.argv[1],
1924   * or require.main.filename, or __filename.
1925   *
1926   * (Used internally and public although not documented in README.)
1927   *
1928   * @example
1929   * program.nameFromFilename(require.main.filename);
1930   *
1931   * @param {string} filename
1932   * @return {Command}
1933   */
1934
1935  nameFromFilename(filename) {
1936    this._name = path.basename(filename, path.extname(filename));
1937
1938    return this;
1939  }
1940
1941  /**
1942   * Get or set the directory for searching for executable subcommands of this command.
1943   *
1944   * @example
1945   * program.executableDir(__dirname);
1946   * // or
1947   * program.executableDir('subcommands');
1948   *
1949   * @param {string} [path]
1950   * @return {string|Command}
1951   */
1952
1953  executableDir(path) {
1954    if (path === undefined) return this._executableDir;
1955    this._executableDir = path;
1956    return this;
1957  }
1958
1959  /**
1960   * Return program help documentation.
1961   *
1962   * @param {{ error: boolean }} [contextOptions] - pass {error:true} to wrap for stderr instead of stdout
1963   * @return {string}
1964   */
1965
1966  helpInformation(contextOptions) {
1967    const helper = this.createHelp();
1968    if (helper.helpWidth === undefined) {
1969      helper.helpWidth = (contextOptions && contextOptions.error) ? this._outputConfiguration.getErrHelpWidth() : this._outputConfiguration.getOutHelpWidth();
1970    }
1971    return helper.formatHelp(this, helper);
1972  }
1973
1974  /**
1975   * @api private
1976   */
1977
1978  _getHelpContext(contextOptions) {
1979    contextOptions = contextOptions || {};
1980    const context = { error: !!contextOptions.error };
1981    let write;
1982    if (context.error) {
1983      write = (arg) => this._outputConfiguration.writeErr(arg);
1984    } else {
1985      write = (arg) => this._outputConfiguration.writeOut(arg);
1986    }
1987    context.write = contextOptions.write || write;
1988    context.command = this;
1989    return context;
1990  }
1991
1992  /**
1993   * Output help information for this command.
1994   *
1995   * Outputs built-in help, and custom text added using `.addHelpText()`.
1996   *
1997   * @param {{ error: boolean } | Function} [contextOptions] - pass {error:true} to write to stderr instead of stdout
1998   */
1999
2000  outputHelp(contextOptions) {
2001    let deprecatedCallback;
2002    if (typeof contextOptions === 'function') {
2003      deprecatedCallback = contextOptions;
2004      contextOptions = undefined;
2005    }
2006    const context = this._getHelpContext(contextOptions);
2007
2008    getCommandAndParents(this).reverse().forEach(command => command.emit('beforeAllHelp', context));
2009    this.emit('beforeHelp', context);
2010
2011    let helpInformation = this.helpInformation(context);
2012    if (deprecatedCallback) {
2013      helpInformation = deprecatedCallback(helpInformation);
2014      if (typeof helpInformation !== 'string' && !Buffer.isBuffer(helpInformation)) {
2015        throw new Error('outputHelp callback must return a string or a Buffer');
2016      }
2017    }
2018    context.write(helpInformation);
2019
2020    this.emit(this._helpLongFlag); // deprecated
2021    this.emit('afterHelp', context);
2022    getCommandAndParents(this).forEach(command => command.emit('afterAllHelp', context));
2023  }
2024
2025  /**
2026   * You can pass in flags and a description to override the help
2027   * flags and help description for your command. Pass in false to
2028   * disable the built-in help option.
2029   *
2030   * @param {string | boolean} [flags]
2031   * @param {string} [description]
2032   * @return {Command} `this` command for chaining
2033   */
2034
2035  helpOption(flags, description) {
2036    if (typeof flags === 'boolean') {
2037      this._hasHelpOption = flags;
2038      return this;
2039    }
2040    this._helpFlags = flags || this._helpFlags;
2041    this._helpDescription = description || this._helpDescription;
2042
2043    const helpFlags = splitOptionFlags(this._helpFlags);
2044    this._helpShortFlag = helpFlags.shortFlag;
2045    this._helpLongFlag = helpFlags.longFlag;
2046
2047    return this;
2048  }
2049
2050  /**
2051   * Output help information and exit.
2052   *
2053   * Outputs built-in help, and custom text added using `.addHelpText()`.
2054   *
2055   * @param {{ error: boolean }} [contextOptions] - pass {error:true} to write to stderr instead of stdout
2056   */
2057
2058  help(contextOptions) {
2059    this.outputHelp(contextOptions);
2060    let exitCode = process.exitCode || 0;
2061    if (exitCode === 0 && contextOptions && typeof contextOptions !== 'function' && contextOptions.error) {
2062      exitCode = 1;
2063    }
2064    // message: do not have all displayed text available so only passing placeholder.
2065    this._exit(exitCode, 'commander.help', '(outputHelp)');
2066  }
2067
2068  /**
2069   * Add additional text to be displayed with the built-in help.
2070   *
2071   * Position is 'before' or 'after' to affect just this command,
2072   * and 'beforeAll' or 'afterAll' to affect this command and all its subcommands.
2073   *
2074   * @param {string} position - before or after built-in help
2075   * @param {string | Function} text - string to add, or a function returning a string
2076   * @return {Command} `this` command for chaining
2077   */
2078  addHelpText(position, text) {
2079    const allowedValues = ['beforeAll', 'before', 'after', 'afterAll'];
2080    if (!allowedValues.includes(position)) {
2081      throw new Error(`Unexpected value for position to addHelpText.
2082Expecting one of '${allowedValues.join("', '")}'`);
2083    }
2084    const helpEvent = `${position}Help`;
2085    this.on(helpEvent, (context) => {
2086      let helpStr;
2087      if (typeof text === 'function') {
2088        helpStr = text({ error: context.error, command: context.command });
2089      } else {
2090        helpStr = text;
2091      }
2092      // Ignore falsy value when nothing to output.
2093      if (helpStr) {
2094        context.write(`${helpStr}\n`);
2095      }
2096    });
2097    return this;
2098  }
2099}
2100
2101/**
2102 * Output help information if help flags specified
2103 *
2104 * @param {Command} cmd - command to output help for
2105 * @param {Array} args - array of options to search for help flags
2106 * @api private
2107 */
2108
2109function outputHelpIfRequested(cmd, args) {
2110  const helpOption = cmd._hasHelpOption && args.find(arg => arg === cmd._helpLongFlag || arg === cmd._helpShortFlag);
2111  if (helpOption) {
2112    cmd.outputHelp();
2113    // (Do not have all displayed text available so only passing placeholder.)
2114    cmd._exit(0, 'commander.helpDisplayed', '(outputHelp)');
2115  }
2116}
2117
2118/**
2119 * Scan arguments and increment port number for inspect calls (to avoid conflicts when spawning new command).
2120 *
2121 * @param {string[]} args - array of arguments from node.execArgv
2122 * @returns {string[]}
2123 * @api private
2124 */
2125
2126function incrementNodeInspectorPort(args) {
2127  // Testing for these options:
2128  //  --inspect[=[host:]port]
2129  //  --inspect-brk[=[host:]port]
2130  //  --inspect-port=[host:]port
2131  return args.map((arg) => {
2132    if (!arg.startsWith('--inspect')) {
2133      return arg;
2134    }
2135    let debugOption;
2136    let debugHost = '127.0.0.1';
2137    let debugPort = '9229';
2138    let match;
2139    if ((match = arg.match(/^(--inspect(-brk)?)$/)) !== null) {
2140      // e.g. --inspect
2141      debugOption = match[1];
2142    } else if ((match = arg.match(/^(--inspect(-brk|-port)?)=([^:]+)$/)) !== null) {
2143      debugOption = match[1];
2144      if (/^\d+$/.test(match[3])) {
2145        // e.g. --inspect=1234
2146        debugPort = match[3];
2147      } else {
2148        // e.g. --inspect=localhost
2149        debugHost = match[3];
2150      }
2151    } else if ((match = arg.match(/^(--inspect(-brk|-port)?)=([^:]+):(\d+)$/)) !== null) {
2152      // e.g. --inspect=localhost:1234
2153      debugOption = match[1];
2154      debugHost = match[3];
2155      debugPort = match[4];
2156    }
2157
2158    if (debugOption && debugPort !== '0') {
2159      return `${debugOption}=${debugHost}:${parseInt(debugPort) + 1}`;
2160    }
2161    return arg;
2162  });
2163}
2164
2165/**
2166 * @param {Command} startCommand
2167 * @returns {Command[]}
2168 * @api private
2169 */
2170
2171function getCommandAndParents(startCommand) {
2172  const result = [];
2173  for (let command = startCommand; command; command = command.parent) {
2174    result.push(command);
2175  }
2176  return result;
2177}
2178
2179exports.Command = Command;
2180