diff --git a/esm.mjs b/esm.mjs index 2206bddd4..5ed09724e 100644 --- a/esm.mjs +++ b/esm.mjs @@ -1,4 +1,15 @@ import commander from './index.js'; // wrapper to provide named exports for ESM. -export const { program, Option, Command, Argument, CommanderError, InvalidOptionArgumentError, Help, createCommand, createOption } = commander; +export const { + program, + createCommand, + createArgument, + createOption, + CommanderError, + InvalidOptionArgumentError, + Command, + Argument, + Option, + Help +} = commander; diff --git a/index.js b/index.js index 9c56eab07..743ba42db 100644 --- a/index.js +++ b/index.js @@ -836,6 +836,21 @@ class Command extends EventEmitter { return this; }; + /** + * Factory routine to create a new unattached argument. + * + * See .argument() for creating an attached argument, which uses this routine to + * create the argument. You can override createArgument to return a custom argument. + * + * @param {string} name + * @param {string} [description] + * @return {Argument} new argument + */ + + createArgument(name, description) { + return new Argument(name, description); + }; + /** * Define argument syntax for command. * @@ -852,7 +867,7 @@ class Command extends EventEmitter { * @return {Command} `this` command for chaining */ argument(name, description) { - const argument = new Argument(name, description); + const argument = this.createArgument(name, description); this.addArgument(argument); return this; } diff --git a/tests/command.createArgument.test.js b/tests/command.createArgument.test.js new file mode 100644 index 000000000..ae235bf0b --- /dev/null +++ b/tests/command.createArgument.test.js @@ -0,0 +1,40 @@ +const commander = require('../'); + +class MyArgument extends commander.Argument { + constructor(name, description) { + super(name, description); + this.myProperty = 'MyArgument'; + }; +} + +class MyCommand extends commander.Command { + createArgument(name, description) { + return new MyArgument(name, description); + }; + + // createCommand for testing .command('sub ') + createCommand(name) { + return new MyCommand(name); + } +} + +test('when override createArgument then used for argument()', () => { + const program = new MyCommand(); + program.argument(''); + expect(program._args.length).toEqual(1); + expect(program._args[0].myProperty).toEqual('MyArgument'); +}); + +test('when override createArgument then used for arguments()', () => { + const program = new MyCommand(); + program.arguments(''); + expect(program._args.length).toEqual(1); + expect(program._args[0].myProperty).toEqual('MyArgument'); +}); + +test('when override createArgument and createCommand then used for argument of command()', () => { + const program = new MyCommand(); + const sub = program.command('sub '); + expect(sub._args.length).toEqual(1); + expect(sub._args[0].myProperty).toEqual('MyArgument'); +}); diff --git a/tests/esm-imports-test.mjs b/tests/esm-imports-test.mjs index 222da7777..03b25bb8c 100644 --- a/tests/esm-imports-test.mjs +++ b/tests/esm-imports-test.mjs @@ -1,4 +1,4 @@ -import { program, Command, Option, Argument, CommanderError, InvalidOptionArgumentError, Help, createCommand, createOption } from '../esm.mjs'; +import { program, Command, Option, Argument, CommanderError, InvalidOptionArgumentError, Help, createCommand, createArgument, createOption } from '../esm.mjs'; // Do some simple checks that expected imports are available at runtime. // Run using `npm run test-esm`. @@ -30,6 +30,8 @@ checkClass(new Argument(''), 'Argument'); console.log('Checking createCommand'); check(typeof createCommand === 'function', 'createCommand is function'); +console.log('Checking createArgument'); +check(typeof createArgument === 'function', 'createArgument is function'); console.log('Checking createOption'); check(typeof createOption === 'function', 'createOption is function'); diff --git a/typings/index.d.ts b/typings/index.d.ts index 4fafd0574..904c48704 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -249,6 +249,14 @@ declare namespace commander { */ addCommand(cmd: Command, opts?: CommandOptions): this; + /** + * Factory routine to create a new unattached argument. + * + * See .argument() for creating an attached argument, which uses this routine to + * create the argument. You can override createArgument to return a custom argument. + */ + createArgument(name: string, description?: string): Argument; + /** * Define argument syntax for command. * diff --git a/typings/index.test-d.ts b/typings/index.test-d.ts index b251b0a96..4e42c5854 100644 --- a/typings/index.test-d.ts +++ b/typings/index.test-d.ts @@ -345,3 +345,7 @@ expectType(baseArgument.variadic); // Argument methods // name expectType(baseArgument.name()); + +// createArgument +expectType(program.createArgument('')); +expectType(program.createArgument('', 'description'));