From e8bea4aedd750418d094591d63699010f5c7b623 Mon Sep 17 00:00:00 2001 From: Wee Bit Date: Thu, 3 Aug 2023 13:04:15 +0300 Subject: [PATCH 1/4] Fix version() parameter type Borrowed from a3f0e28 that was supposed to land in the now-closed #1921. --- lib/command.js | 2 +- typings/index.d.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/command.js b/lib/command.js index 590a271dd..e014d957f 100644 --- a/lib/command.js +++ b/lib/command.js @@ -1807,7 +1807,7 @@ Expecting one of '${allowedValues.join("', '")}'`); * * You can optionally supply the flags and description to override the defaults. * - * @param {string} str + * @param {string} [str] * @param {string} [flags] * @param {string} [description] * @return {this | string} `this` command for chaining, or version string if no arguments diff --git a/typings/index.d.ts b/typings/index.d.ts index 695c3bd25..73c606a5d 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -293,7 +293,7 @@ export class Command { * * You can optionally supply the flags and description to override the defaults. */ - version(str: string, flags?: string, description?: string): this; + version(str?: string, flags?: string, description?: string): this; /** * Define a command, implemented using an action handler. From b488e6e08f6270c7992a9cc4f643401a7d273949 Mon Sep 17 00:00:00 2001 From: Wee Bit Date: Thu, 3 Aug 2023 20:06:13 +0300 Subject: [PATCH 2/4] Initialize _version, _versionOptionName in constructor The _version and _versionOptionName properties are initialized to undefined in the constructor. The reasoning is - to make them visible when the Command instance is logged into the console before .version() is called, and not only appear after suddenly - to not break anything if the proxy use for consistent option value handling in the legacy setOptionValueWithSource mode suggested in #1919 (and previously in #1921) is adopted --- lib/command.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/command.js b/lib/command.js index e014d957f..ca4a45ecb 100644 --- a/lib/command.js +++ b/lib/command.js @@ -77,6 +77,9 @@ class Command extends EventEmitter { this._helpCommandnameAndArgs = 'help [command]'; this._helpCommandDescription = 'display help for command'; this._helpConfiguration = {}; + + this._version = undefined; + this._versionOptionName = undefined; } /** From 3c94dfd3c081dbb412f79e62730d2d0acb517a83 Mon Sep 17 00:00:00 2001 From: Wee Bit Date: Thu, 3 Aug 2023 20:10:17 +0300 Subject: [PATCH 3/4] Store version option instance instead of just name Storing the Option instance of the version option instead of just its .attributeName() (_versionOptionName) is meaningful for cases when other properties of the instance need to be accessed (not currently the case, but could be in the future). --- lib/command.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/command.js b/lib/command.js index ca4a45ecb..97519d57b 100644 --- a/lib/command.js +++ b/lib/command.js @@ -79,7 +79,7 @@ class Command extends EventEmitter { this._helpConfiguration = {}; this._version = undefined; - this._versionOptionName = undefined; + this._versionOption = undefined; } /** @@ -1562,10 +1562,11 @@ Expecting one of '${allowedValues.join("', '")}'`); // Preserve original behaviour so backwards compatible when still using properties const result = {}; const len = this.options.length; + const versionOptionName = this._versionOption?.attributeName(); for (let i = 0; i < len; i++) { const key = this.options[i].attributeName(); - result[key] = key === this._versionOptionName ? this._version : this[key]; + result[key] = key === versionOptionName ? this._version : this[key]; } return result; } @@ -1821,10 +1822,9 @@ Expecting one of '${allowedValues.join("', '")}'`); this._version = str; flags = flags || '-V, --version'; description = description || 'output the version number'; - const versionOption = this.createOption(flags, description); - this._versionOptionName = versionOption.attributeName(); - this.options.push(versionOption); - this.on('option:' + versionOption.name(), () => { + this._versionOption = this.createOption(flags, description); + this.options.push(this._versionOption); + this.on('option:' + this._versionOption.name(), () => { this._outputConfiguration.writeOut(`${str}\n`); this._exit(0, 'commander.version', str); }); From 2f3c4c3c2e13f234ef584620f83faa1cf02c8001 Mon Sep 17 00:00:00 2001 From: Wee Bit Date: Thu, 3 Aug 2023 21:04:11 +0300 Subject: [PATCH 4/4] Allow version number and option's flags and description updates --- lib/command.js | 28 ++++++++++++++--------- lib/option.js | 60 ++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 64 insertions(+), 24 deletions(-) diff --git a/lib/command.js b/lib/command.js index 97519d57b..b6291abeb 100644 --- a/lib/command.js +++ b/lib/command.js @@ -1818,16 +1818,24 @@ Expecting one of '${allowedValues.join("', '")}'`); */ version(str, flags, description) { - if (str === undefined) return this._version; - this._version = str; - flags = flags || '-V, --version'; - description = description || 'output the version number'; - this._versionOption = this.createOption(flags, description); - this.options.push(this._versionOption); - this.on('option:' + this._versionOption.name(), () => { - this._outputConfiguration.writeOut(`${str}\n`); - this._exit(0, 'commander.version', str); - }); + if (str === undefined && flags === undefined && description === undefined) { + return this._version; + } + if (str !== undefined) { + this._version = str; + } + if (!this._versionOption) { + flags = flags || '-V, --version'; + description = description || 'output the version number'; + this._versionOption = this.createOption(flags, description); + this.options.push(this._versionOption); + this.on('option:' + this._versionOption.name(), () => { + this._outputConfiguration.writeOut(`${this._version}\n`); + this._exit(0, 'commander.version', this._version); + }); + } else { + this._versionOption.setFlagsAndDescription(flags, description); + } return this; } diff --git a/lib/option.js b/lib/option.js index d61fc5f2f..e4d7c0bf8 100644 --- a/lib/option.js +++ b/lib/option.js @@ -11,21 +11,8 @@ class Option { */ constructor(flags, description) { - this.flags = flags; - this.description = description || ''; - - this.required = flags.includes('<'); // A value must be supplied when the option is specified. - this.optional = flags.includes('['); // A value is optional when the option is specified. - // variadic test ignores et al which might be used to describe custom splitting of single argument - this.variadic = /\w\.\.\.[>\]]$/.test(flags); // The option can take multiple values. + this.setFlagsAndDescription(flags, description); this.mandatory = false; // The option must have a value after parsing, which usually means it must be specified on command line. - const optionFlags = splitOptionFlags(flags); - this.short = optionFlags.shortFlag; - this.long = optionFlags.longFlag; - this.negate = false; - if (this.long) { - this.negate = this.long.startsWith('--no-'); - } this.defaultValue = undefined; this.defaultValueDescription = undefined; this.presetArg = undefined; @@ -37,6 +24,51 @@ class Option { this.implied = undefined; } + /** + * Set the option flags and description, if provided. + * Leave unchanged otherwise. + * + * @param {string} [flags] + * @param {string} [description] + */ + + setFlagsAndDescription(flags, description) { + this.setFlags(flags); + this.setDescription(description); + } + + /** + * Set the option flags, if provided. + * Leave unchanged otherwise. + * + * @param {string} [flags] + */ + + setFlags(flags) { + if (flags !== undefined) { + this.flags = flags; + this.required = flags.includes('<'); // A value must be supplied when the option is specified. + this.optional = flags.includes('['); // A value is optional when the option is specified. + // variadic test ignores et al which might be used to describe custom splitting of single argument + this.variadic = /\w\.\.\.[>\]]$/.test(flags); // The option can take multiple values. + const optionFlags = splitOptionFlags(flags); + this.short = optionFlags.shortFlag; + this.long = optionFlags.longFlag; + this.negate = this.long ? this.long.startsWith('--no-') : false; + } + } + + /** + * Set the option description, if provided. + * Leave unchanged otherwise. + * + * @param {string} [description] + */ + + setDescription(description) { + this.description = description || this.description || ''; + } + /** * Set the default value, and optionally supply the description to be displayed in the help. *