diff --git a/lib/argument.js b/lib/argument.js index c16430250..d7acadfb4 100644 --- a/lib/argument.js +++ b/lib/argument.js @@ -99,8 +99,9 @@ class Argument { choices(values) { this.argChoices = values.slice(); this.parseArg = (arg, previous) => { - if (!this.argChoices.includes(arg)) { - throw new InvalidArgumentError(`Allowed choices are ${this.argChoices.join(', ')}.`); + const argChoices = /** @type {string[]} */ (this.argChoices); + if (!argChoices.includes(arg)) { + throw new InvalidArgumentError(`Allowed choices are ${argChoices.join(', ')}.`); } if (this.variadic) { return this._concatValue(arg, previous); diff --git a/lib/command.js b/lib/command.js index 590a271dd..edd7e4285 100644 --- a/lib/command.js +++ b/lib/command.js @@ -142,7 +142,9 @@ class Command extends EventEmitter { desc = null; } opts = opts || {}; - const [, name, args] = nameAndArgs.match(/([^ ]+) *(.*)/); + const [, name, args] = /** @type {[*, string, string | undefined]} */ ( + nameAndArgs.match(/([^ ]+) *(.*)/) + ); const cmd = this.createCommand(name); if (desc) { @@ -385,7 +387,9 @@ class Command extends EventEmitter { _hasImplicitHelpCommand() { if (this._addImplicitHelpCommand === undefined) { - return this.commands.length && !this._actionHandler && !this._findCommand('help'); + return Boolean( + this.commands.length && !this._actionHandler && !this._findCommand('help') + ); } return this._addImplicitHelpCommand; } @@ -538,7 +542,7 @@ Expecting one of '${allowedValues.join("', '")}'`); try { val = option.parseArg(val, oldValue); } catch (err) { - if (err.code === 'commander.invalidArgument') { + if (err instanceof CommanderError && err.code === 'commander.invalidArgument') { const message = `${invalidValueMessage} ${err.message}`; this.error(message, { exitCode: err.exitCode, code: err.code }); } @@ -780,7 +784,7 @@ Expecting one of '${allowedValues.join("', '")}'`); */ setOptionValue(key, value) { - return this.setOptionValueWithSource(key, value, undefined); + return this.setOptionValueWithSource(key, value); } /** @@ -788,7 +792,7 @@ Expecting one of '${allowedValues.join("', '")}'`); * * @param {string} key * @param {Object} value - * @param {string} source - expected values are default/config/env/cli/implied + * @param {string} [source] - expected values are default/config/env/cli/implied * @return {Command} `this` command for chaining */ @@ -807,7 +811,7 @@ Expecting one of '${allowedValues.join("', '")}'`); * Expected values are default | config | env | cli | implied * * @param {string} key - * @return {string} + * @return {string | undefined} */ getOptionValueSource(key) { @@ -819,7 +823,7 @@ Expecting one of '${allowedValues.join("', '")}'`); * Expected values are default | config | env | cli | implied * * @param {string} key - * @return {string} + * @return {string | undefined} */ getOptionValueSourceWithGlobals(key) { @@ -1070,7 +1074,7 @@ Expecting one of '${allowedValues.join("', '")}'`); _dispatchSubcommand(commandName, operands, unknown) { const subCommand = this._findCommand(commandName); - if (!subCommand) this.help({ error: true }); + if (!subCommand) return this.help({ error: true }); let hookResult; hookResult = this._chainOrCallSubCommandHook(hookResult, subCommand, 'preSubcommand'); @@ -1093,11 +1097,11 @@ Expecting one of '${allowedValues.join("', '")}'`); _dispatchHelpCommand(subcommandName) { if (!subcommandName) { - this.help(); + return this.help(); } const subCommand = this._findCommand(subcommandName); if (subCommand && !subCommand._executableHandler) { - subCommand.help(); + return subCommand.help(); } // Fallback to parsing the help flag to invoke the help. @@ -1140,7 +1144,7 @@ Expecting one of '${allowedValues.join("', '")}'`); try { parsedValue = argument.parseArg(value, previous); } catch (err) { - if (err.code === 'commander.invalidArgument') { + if (err instanceof CommanderError && err.code === 'commander.invalidArgument') { const message = `error: command-argument value '${value}' is invalid for argument '${argument.name()}'. ${err.message}`; this.error(message, { exitCode: err.exitCode, code: err.code }); } @@ -1297,7 +1301,9 @@ Expecting one of '${allowedValues.join("', '")}'`); let actionResult; actionResult = this._chainOrCallHooks(actionResult, 'preAction'); - actionResult = this._chainOrCall(actionResult, () => this._actionHandler(this.processedArgs)); + actionResult = this._chainOrCall(actionResult, () => ( + /** @type {Function} */ (this._actionHandler)(this.processedArgs) + )); if (this.parent) { actionResult = this._chainOrCall(actionResult, () => { this.parent.emit(commandEvent, operands, unknown); // legacy @@ -1348,7 +1354,7 @@ Expecting one of '${allowedValues.join("', '")}'`); * Return an option matching `arg` if any. * * @param {string} arg - * @return {Option} + * @return {Option | undefined} * @api private */ @@ -1446,7 +1452,7 @@ Expecting one of '${allowedValues.join("', '")}'`); // parse options let activeVariadicOption = null; while (args.length) { - const arg = args.shift(); + const arg = /** @type {string} */ (args.shift()); // literal if (arg === '--') { @@ -1619,7 +1625,11 @@ Expecting one of '${allowedValues.join("', '")}'`); if (option.envVar && option.envVar in process.env) { const optionKey = option.attributeName(); // Priority check. Do not overwrite cli or options from unknown source (client-code). - if (this.getOptionValue(optionKey) === undefined || ['default', 'config', 'env'].includes(this.getOptionValueSource(optionKey))) { + if (this.getOptionValue(optionKey) === undefined || ( + /** @type {Array} */ (['default', 'config', 'env']).includes( + this.getOptionValueSource(optionKey) + ) + )) { if (option.required || option.optional) { // option can take a value // keep very simple, optional always takes value this.emit(`optionEnv:${option.name()}`, process.env[option.envVar]); @@ -1640,7 +1650,11 @@ Expecting one of '${allowedValues.join("', '")}'`); _parseOptionsImplied() { const dualHelper = new DualOptions(this.options); const hasCustomOptionValue = (optionKey) => { - return this.getOptionValue(optionKey) !== undefined && !['default', 'implied'].includes(this.getOptionValueSource(optionKey)); + return this.getOptionValue(optionKey) !== undefined && !( + /** @type {Array} */ (['default', 'implied']).includes( + this.getOptionValueSource(optionKey) + ) + ); }; this.options .filter(option => (option.implied !== undefined) && @@ -1810,7 +1824,7 @@ Expecting one of '${allowedValues.join("', '")}'`); * @param {string} str * @param {string} [flags] * @param {string} [description] - * @return {this | string} `this` command for chaining, or version string if no arguments + * @return {this | string | undefined} `this` command for chaining, or version string if no arguments */ version(str, flags, description) { @@ -1837,7 +1851,9 @@ Expecting one of '${allowedValues.join("', '")}'`); */ description(str, argsDescription) { if (str === undefined && argsDescription === undefined) return this._description; - this._description = str; + if (str) { + this._description = str; + } if (argsDescription) { this._argsDescription = argsDescription; } @@ -1912,11 +1928,11 @@ Expecting one of '${allowedValues.join("', '")}'`); const args = this._args.map((arg) => { return humanReadableArgName(arg); }); - return [].concat( - (this.options.length || this._hasHelpOption ? '[options]' : []), - (this.commands.length ? '[command]' : []), - (this._args.length ? args : []) - ).join(' '); + return [ + ...(this.options.length || this._hasHelpOption ? ['[options]'] : []), + ...(this.commands.length ? ['[command]'] : []), + ...(this._args.length ? args : []) + ].join(' '); } this._usage = str; @@ -1964,7 +1980,7 @@ Expecting one of '${allowedValues.join("', '")}'`); * program.executableDir('subcommands'); * * @param {string} [path] - * @return {string|Command} + * @return {string|null|Command} */ executableDir(path) { diff --git a/lib/error.js b/lib/error.js index e7cde9cc7..7f166a3bd 100644 --- a/lib/error.js +++ b/lib/error.js @@ -9,7 +9,7 @@ class CommanderError extends Error { * Constructs the CommanderError class * @param {number} exitCode suggested exit code which could be used with process.exit * @param {string} code an id string representing the error - * @param {string} message human-readable description of the error + * @param {string} [message] human-readable description of the error * @constructor */ constructor(exitCode, code, message) { diff --git a/lib/help.js b/lib/help.js index 14e0fb9f3..e98c90993 100644 --- a/lib/help.js +++ b/lib/help.js @@ -30,7 +30,9 @@ class Help { const visibleCommands = cmd.commands.filter(cmd => !cmd._hidden); if (cmd._hasImplicitHelpCommand()) { // Create a command matching the implicit help command. - const [, helpName, helpArgs] = cmd._helpCommandnameAndArgs.match(/([^ ]+) *(.*)/); + const [, helpName, helpArgs] = /** @type {[*, string, string | undefined]} */ ( + cmd._helpCommandnameAndArgs.match(/([^ ]+) *(.*)/) + ); const helpCommand = cmd.createCommand(helpName) .helpOption(false); helpCommand.description(cmd._helpCommandDescription); diff --git a/lib/option.js b/lib/option.js index d61fc5f2f..b48a3404d 100644 --- a/lib/option.js +++ b/lib/option.js @@ -181,8 +181,9 @@ class Option { choices(values) { this.argChoices = values.slice(); this.parseArg = (arg, previous) => { - if (!this.argChoices.includes(arg)) { - throw new InvalidArgumentError(`Allowed choices are ${this.argChoices.join(', ')}.`); + const argChoices = /** @type {string[]} */ (this.argChoices); + if (!argChoices.includes(arg)) { + throw new InvalidArgumentError(`Allowed choices are ${argChoices.join(', ')}.`); } if (this.variadic) { return this._concatValue(arg, previous); diff --git a/typings/index.d.ts b/typings/index.d.ts index 695c3bd25..2bdbe0cca 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -595,17 +595,17 @@ export class Command { /** * Store option value and where the value came from. */ - setOptionValueWithSource(key: string, value: unknown, source: OptionValueSource): this; + setOptionValueWithSource(key: string, value: unknown, source?: OptionValueSource | string | undefined): this; /** * Get source of option value. */ - getOptionValueSource(key: string): OptionValueSource | undefined; + getOptionValueSource(key: string): OptionValueSource | string | undefined; /** * Get source of option value. See also .optsWithGlobals(). */ - getOptionValueSourceWithGlobals(key: string): OptionValueSource | undefined; + getOptionValueSourceWithGlobals(key: string): OptionValueSource | string | undefined; /** * Alter parsing of short flags with optional values. @@ -819,7 +819,7 @@ export class Command { /** * Get the executable search directory. */ - executableDir(): string; + executableDir(): string | null; /** * Output help information for this command. diff --git a/typings/index.test-d.ts b/typings/index.test-d.ts index 734036fad..13cdbfe24 100644 --- a/typings/index.test-d.ts +++ b/typings/index.test-d.ts @@ -173,10 +173,10 @@ expectType(program.setOptionValue('example', true)); expectType(program.setOptionValueWithSource('example', [], 'cli')); // getOptionValueSource -expectType(program.getOptionValueSource('example')); +expectType(program.getOptionValueSource('example')); // getOptionValueSourceWithGlobals -expectType(program.getOptionValueSourceWithGlobals('example')); +expectType(program.getOptionValueSourceWithGlobals('example')); // combineFlagAndOptionalValue expectType(program.combineFlagAndOptionalValue()); @@ -275,7 +275,7 @@ expectType(program.nameFromFilename(__filename)); // executableDir expectType(program.executableDir(__dirname)); -expectType(program.executableDir()); +expectType(program.executableDir()); // outputHelp // eslint-disable-next-line @typescript-eslint/no-invalid-void-type, @typescript-eslint/no-confusing-void-expression