diff --git a/index.js b/index.js index 2ae841538..44b5e2708 100644 --- a/index.js +++ b/index.js @@ -117,7 +117,7 @@ class Command extends EventEmitter { this._executableFile = null; // custom name for executable this._defaultCommandName = null; this._exitCallback = null; - this._alias = null; + this._aliases = []; this._hidden = false; this._helpFlags = '-h, --help'; @@ -937,7 +937,7 @@ class Command extends EventEmitter { */ _findCommand(name) { if (!name) return undefined; - return this.commands.find(cmd => cmd._name === name || cmd._alias === name); + return this.commands.find(cmd => cmd._name === name || cmd._aliases.includes(name)); }; /** @@ -1201,7 +1201,7 @@ class Command extends EventEmitter { * * @param {string} str * @param {Object} [argsDescription] - * @return {String|Command} + * @return {string|Command} * @api public */ @@ -1213,15 +1213,17 @@ class Command extends EventEmitter { }; /** - * Set an alias for the command + * Set an alias for the command. * - * @param {string} alias - * @return {String|Command} + * You may call more than once to add multiple aliases. Only the first alias is shown in the auto-generated help. + * + * @param {string} [alias] + * @return {string|Command} * @api public */ alias(alias) { - if (alias === undefined) return this._alias; + if (alias === undefined) return this._aliases[0]; // just return first, for backwards compatibility let command = this; if (this.commands.length !== 0 && this.commands[this.commands.length - 1]._executableHandler) { @@ -1231,7 +1233,25 @@ class Command extends EventEmitter { if (alias === command._name) throw new Error('Command alias can\'t be the same as its name'); - command._alias = alias; + command._aliases.push(alias); + return this; + }; + + /** + * Set aliases for the command. + * + * Only the first alias is shown in the auto-generated help. + * + * @param {string[]} [aliases] + * @return {string[]|Command} + * @api public + */ + + aliases(aliases) { + // Getter for the array of aliases is the main reason for having aliases() in addition to alias(). + if (aliases === undefined) return this._aliases; + + aliases.forEach((alias) => this.alias(alias)); return this; }; @@ -1290,7 +1310,7 @@ class Command extends EventEmitter { return [ cmd._name + - (cmd._alias ? '|' + cmd._alias : '') + + (cmd._aliases[0] ? '|' + cmd._aliases[0] : '') + (cmd.options.length ? ' [options]' : '') + (args ? ' ' + args : ''), cmd._description @@ -1450,8 +1470,8 @@ class Command extends EventEmitter { } let cmdName = this._name; - if (this._alias) { - cmdName = cmdName + '|' + this._alias; + if (this._aliases[0]) { + cmdName = cmdName + '|' + this._aliases[0]; } let parentCmdNames = ''; for (let parentCmd = this.parent; parentCmd; parentCmd = parentCmd.parent) { diff --git a/tests/command.alias.test.js b/tests/command.alias.test.js index f5577905a..1c320f5d0 100644 --- a/tests/command.alias.test.js +++ b/tests/command.alias.test.js @@ -12,7 +12,26 @@ test('when command has alias then appears in help', () => { expect(helpInformation).toMatch('info|i'); }); -test('when command = alias then error', () => { +test('when command has aliases added separately then only first appears in help', () => { + const program = new commander.Command(); + program + .command('list [thing]') + .alias('ls') + .alias('dir'); + const helpInformation = program.helpInformation(); + expect(helpInformation).toMatch('list|ls '); +}); + +test('when command has aliases then only first appears in help', () => { + const program = new commander.Command(); + program + .command('list [thing]') + .aliases(['ls', 'dir']); + const helpInformation = program.helpInformation(); + expect(helpInformation).toMatch('list|ls '); +}); + +test('when command name = alias then error', () => { const program = new commander.Command(); expect(() => { program @@ -20,3 +39,37 @@ test('when command = alias then error', () => { .alias('fail'); }).toThrow("Command alias can't be the same as its name"); }); + +test('when use alias then action handler called', () => { + const program = new commander.Command(); + const actionMock = jest.fn(); + program + .command('list') + .alias('ls') + .action(actionMock); + program.parse(['ls'], { from: 'user' }); + expect(actionMock).toHaveBeenCalled(); +}); + +test('when use second alias added separately then action handler called', () => { + const program = new commander.Command(); + const actionMock = jest.fn(); + program + .command('list') + .alias('ls') + .alias('dir') + .action(actionMock); + program.parse(['dir'], { from: 'user' }); + expect(actionMock).toHaveBeenCalled(); +}); + +test('when use second of aliases then action handler called', () => { + const program = new commander.Command(); + const actionMock = jest.fn(); + program + .command('list') + .aliases(['ls', 'dir']) + .action(actionMock); + program.parse(['dir'], { from: 'user' }); + expect(actionMock).toHaveBeenCalled(); +}); diff --git a/typings/commander-tests.ts b/typings/commander-tests.ts index 5ceda8e13..57392c667 100644 --- a/typings/commander-tests.ts +++ b/typings/commander-tests.ts @@ -169,6 +169,10 @@ const descriptionValue: string = program.description(); const aliasThis: commander.Command = program.alias('my alias'); const aliasValue: string = program.alias(); +// aliases +const aliasesThis: commander.Command = program.aliases(['first-alias', 'second-alias']); +const aliasesValue: string[] = program.aliases(); + // usage const usageThis: commander.Command = program.usage('my usage'); const usageValue: string = program.usage(); diff --git a/typings/index.d.ts b/typings/index.d.ts index d6b4eed41..6f41bf3cf 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -275,6 +275,8 @@ declare namespace commander { /** * Set an alias for the command. * + * You may call more than once to add multiple aliases. Only the first alias is shown in the auto-generated help. + * * @returns `this` command for chaining */ alias(alias: string): this; @@ -283,6 +285,19 @@ declare namespace commander { */ alias(): string; + /** + * Set aliases for the command. + * + * Only the first alias is shown in the auto-generated help. + * + * @returns `this` command for chaining + */ + aliases(aliases: string[]): this; + /** + * Get aliases for the command. + */ + aliases(): string[]; + /** * Set the command usage. *