diff --git a/index.js b/index.js index 922748263..09338b5f1 100644 --- a/index.js +++ b/index.js @@ -58,11 +58,11 @@ class Help { if (showShortHelpFlag || showLongHelpFlag) { let helpOption; if (!showShortHelpFlag) { - helpOption = new Option(cmd._helpLongFlag, cmd._helpDescription); + helpOption = cmd.createOption(cmd._helpLongFlag, cmd._helpDescription); } else if (!showLongHelpFlag) { - helpOption = new Option(cmd._helpShortFlag, cmd._helpDescription); + helpOption = cmd.createOption(cmd._helpShortFlag, cmd._helpDescription); } else { - helpOption = new Option(cmd._helpFlags, cmd._helpDescription); + helpOption = cmd.createOption(cmd._helpFlags, cmd._helpDescription); } visibleOptions.push(helpOption); } @@ -912,6 +912,21 @@ Read more on https://git.io/JJc0W`); } }; + /** + * Factory routine to create a new unattached option. + * + * See .option() for creating an attached option, which uses this routine to + * create the option. You can override createOption to return a custom option. + * + * @param {string} flags + * @param {string} [description] + * @return {Option} new option + */ + + createOption(flags, description) { + return new Option(flags, description); + }; + /** * Add an option. * @@ -991,7 +1006,7 @@ Read more on https://git.io/JJc0W`); * @api private */ _optionEx(config, flags, description, fn, defaultValue) { - const option = new Option(flags, description); + const option = this.createOption(flags, description); option.makeOptionMandatory(!!config.mandatory); if (typeof fn === 'function') { option.default(defaultValue).argParser(fn); @@ -1055,7 +1070,7 @@ Read more on https://git.io/JJc0W`); * program.option('-c, --cheese [type]', 'add cheese [marble]'); * * @param {string} flags - * @param {string} description + * @param {string} [description] * @param {Function|*} [fn] - custom option processing function or default value * @param {*} [defaultValue] * @return {Command} `this` command for chaining @@ -1072,7 +1087,7 @@ Read more on https://git.io/JJc0W`); * The `flags` string contains the short and/or long flags, separated by comma, a pipe or space. * * @param {string} flags - * @param {string} description + * @param {string} [description] * @param {Function|*} [fn] - custom option processing function or default value * @param {*} [defaultValue] * @return {Command} `this` command for chaining @@ -1720,7 +1735,7 @@ Read more on https://git.io/JJc0W`); this._version = str; flags = flags || '-V, --version'; description = description || 'output the version number'; - const versionOption = new Option(flags, description); + const versionOption = this.createOption(flags, description); this._versionOptionName = versionOption.attributeName(); this.options.push(versionOption); this.on('option:' + versionOption.name(), () => { diff --git a/tests/command.createOption.test.js b/tests/command.createOption.test.js new file mode 100644 index 000000000..e72604c45 --- /dev/null +++ b/tests/command.createOption.test.js @@ -0,0 +1,42 @@ +const commander = require('../'); + +class MyOption extends commander.Option { + constructor(flags, description) { + super(flags, description); + this.myProperty = 'MyOption'; + }; +} + +class MyCommand extends commander.Command { + createOption(flags, description) { + return new MyOption(flags, description); + }; +} + +test('when override createOption then used for option()', () => { + const program = new MyCommand(); + program.option('-a, --alpha'); + expect(program.options.length).toEqual(1); + expect(program.options[0].myProperty).toEqual('MyOption'); +}); + +test('when override createOption then used for requiredOption()', () => { + const program = new MyCommand(); + program.requiredOption('-a, --alpha'); + expect(program.options.length).toEqual(1); + expect(program.options[0].myProperty).toEqual('MyOption'); +}); + +test('when override createOption then used for version()', () => { + const program = new MyCommand(); + program.version('1.2.3'); + expect(program.options.length).toEqual(1); + expect(program.options[0].myProperty).toEqual('MyOption'); +}); + +test('when override createOption then used for help option in visibleOptions', () => { + const program = new MyCommand(); + const visibleOptions = program.createHelp().visibleOptions(program); + expect(visibleOptions.length).toEqual(1); + expect(visibleOptions[0].myProperty).toEqual('MyOption'); +}); diff --git a/typings/commander-tests.ts b/typings/commander-tests.ts index a91aa7c1f..6854c3e40 100644 --- a/typings/commander-tests.ts +++ b/typings/commander-tests.ts @@ -135,6 +135,10 @@ const requiredOptionThis9: commander.Command = program.requiredOption('-v, --ver const requiredOptionThis10: commander.Command = program.requiredOption('-c, --collect ', 'repeatable value', collect, []); const requiredOptionThis11: commander.Command = program.requiredOption('-l, --list ', 'comma separated list', commaSeparatedList); +// createOption +const createOption1: commander.Option = program.createOption('a, --alpha'); +const createOption2: commander.Option = program.createOption('a, --alpha', 'description'); + // addOption const addOptionThis: commander.Command = program.addOption(new commander.Option('-s,--simple')); @@ -234,7 +238,6 @@ const onThis: commander.Command = program.on('command:foo', () => { // createCommand -const createInstance1: commander.Command = program.createCommand(); const createInstance2: commander.Command = program.createCommand('name'); class MyCommand extends commander.Command { diff --git a/typings/index.d.ts b/typings/index.d.ts index ebfe63534..a6ee35e5d 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -322,6 +322,15 @@ declare namespace commander { /** @deprecated since v7, instead use choices or a custom function */ requiredOption(flags: string, description: string, regexp: RegExp, defaultValue?: string | boolean): this; + /** + * Factory routine to create a new unattached option. + * + * See .option() for creating an attached option, which uses this routine to + * create the option. You can override createOption to return a custom option. + */ + + createOption(flags: string, description?: string): Option; + /** * Add a prepared Option. *