From d17db259c3620cc8531b4702f87e0baccf39013c Mon Sep 17 00:00:00 2001 From: John Gee Date: Thu, 2 Apr 2020 19:08:03 +1300 Subject: [PATCH 1/4] Allow for multiple aliases #531 --- index.js | 24 +++++++++++++----------- typings/index.d.ts | 2 ++ 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/index.js b/index.js index f58565586..cd40a0826 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._alias = []; 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._alias.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,10 +1213,12 @@ 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 */ @@ -1226,11 +1228,11 @@ class Command extends EventEmitter { command = this.commands[this.commands.length - 1]; } - if (arguments.length === 0) return command._alias; + if (arguments.length === 0) return command._alias[0]; // just return first, and return string not array for backwards compatibility if (alias === command._name) throw new Error('Command alias can\'t be the same as its name'); - command._alias = alias; + command._alias.push(alias); return this; }; @@ -1288,7 +1290,7 @@ class Command extends EventEmitter { return [ cmd._name + - (cmd._alias ? '|' + cmd._alias : '') + + (cmd._alias[0] ? '|' + cmd._alias[0] : '') + (cmd.options.length ? ' [options]' : '') + (args ? ' ' + args : ''), cmd._description @@ -1448,8 +1450,8 @@ class Command extends EventEmitter { } let cmdName = this._name; - if (this._alias) { - cmdName = cmdName + '|' + this._alias; + if (this._alias[0]) { + cmdName = cmdName + '|' + this._alias[0]; } let parentCmdNames = ''; for (let parentCmd = this.parent; parentCmd; parentCmd = parentCmd.parent) { diff --git a/typings/index.d.ts b/typings/index.d.ts index d6b4eed41..8de3f5c82 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; From f6af75132d2ed50920af145f9dd2c7abc9249f77 Mon Sep 17 00:00:00 2001 From: John Gee Date: Thu, 2 Apr 2020 19:38:46 +1300 Subject: [PATCH 2/4] Add tests for multiple alias behaviours --- tests/command.alias.test.js | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/tests/command.alias.test.js b/tests/command.alias.test.js index f5577905a..6f54e375c 100644 --- a/tests/command.alias.test.js +++ b/tests/command.alias.test.js @@ -12,7 +12,17 @@ test('when command has alias then appears in help', () => { expect(helpInformation).toMatch('info|i'); }); -test('when command = alias then error', () => { +test('when command has more than one alias 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 name = alias then error', () => { const program = new commander.Command(); expect(() => { program @@ -20,3 +30,26 @@ 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 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(); +}); From c00fcc6935618ccd6150a81e3fbf723bfd2af5c3 Mon Sep 17 00:00:00 2001 From: John Gee Date: Sun, 5 Apr 2020 09:55:41 +1200 Subject: [PATCH 3/4] Add aliases() mainly for getter --- index.js | 34 ++++++++++++++++++++++++++-------- tests/command.alias.test.js | 24 ++++++++++++++++++++++-- typings/commander-tests.ts | 4 ++++ typings/index.d.ts | 15 ++++++++++++++- 4 files changed, 66 insertions(+), 11 deletions(-) diff --git a/index.js b/index.js index 8dfffbbeb..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 = []; + 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.includes(name)); + return this.commands.find(cmd => cmd._name === name || cmd._aliases.includes(name)); }; /** @@ -1215,7 +1215,7 @@ class Command extends EventEmitter { /** * 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. + * 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} @@ -1223,7 +1223,7 @@ class Command extends EventEmitter { */ alias(alias) { - if (alias === undefined) return this._alias[0]; // just return first, for backwards compatibility + 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) { @@ -1233,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.push(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; }; @@ -1292,7 +1310,7 @@ class Command extends EventEmitter { return [ cmd._name + - (cmd._alias[0] ? '|' + cmd._alias[0] : '') + + (cmd._aliases[0] ? '|' + cmd._aliases[0] : '') + (cmd.options.length ? ' [options]' : '') + (args ? ' ' + args : ''), cmd._description @@ -1452,8 +1470,8 @@ class Command extends EventEmitter { } let cmdName = this._name; - if (this._alias[0]) { - cmdName = cmdName + '|' + this._alias[0]; + 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 6f54e375c..568f3fee7 100644 --- a/tests/command.alias.test.js +++ b/tests/command.alias.test.js @@ -12,7 +12,7 @@ test('when command has alias then appears in help', () => { expect(helpInformation).toMatch('info|i'); }); -test('when command has more than one alias then only first appears in help', () => { +test('when command has aliases added separately then only first appears in help', () => { const program = new commander.Command(); program .command('list [thing]') @@ -22,6 +22,15 @@ test('when command has more than one alias then only first appears in help', () 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(() => { @@ -42,7 +51,7 @@ test('when use alias then action handler called', () => { expect(actionMock).toHaveBeenCalled(); }); -test('when use second alias then action handler called', () => { +test('when use second alias added separately then action handler called', () => { const program = new commander.Command(); const actionMock = jest.fn(); program @@ -53,3 +62,14 @@ test('when use second alias then action handler called', () => { 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') + .alias(['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 8de3f5c82..6f41bf3cf 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -275,7 +275,7 @@ 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. + * 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 */ @@ -285,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. * From 4b35ae2dcf9aef0f0b04e8995417ff0d28eb8a90 Mon Sep 17 00:00:00 2001 From: John Gee Date: Sun, 5 Apr 2020 10:04:29 +1200 Subject: [PATCH 4/4] Fix typo --- tests/command.alias.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/command.alias.test.js b/tests/command.alias.test.js index 568f3fee7..1c320f5d0 100644 --- a/tests/command.alias.test.js +++ b/tests/command.alias.test.js @@ -68,7 +68,7 @@ test('when use second of aliases then action handler called', () => { const actionMock = jest.fn(); program .command('list') - .alias(['ls', 'dir']) + .aliases(['ls', 'dir']) .action(actionMock); program.parse(['dir'], { from: 'user' }); expect(actionMock).toHaveBeenCalled();