Skip to content

Commit 28e8d3f

Browse files
authored
Add support for multiple aliases (#1236)
* Allow for multiple aliases #531 * Add tests for multiple alias behaviours * Add aliases() mainly for getter * Fix typo
1 parent b59adfc commit 28e8d3f

File tree

4 files changed

+104
-12
lines changed

4 files changed

+104
-12
lines changed

index.js

+31-11
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ class Command extends EventEmitter {
117117
this._executableFile = null; // custom name for executable
118118
this._defaultCommandName = null;
119119
this._exitCallback = null;
120-
this._alias = null;
120+
this._aliases = [];
121121

122122
this._hidden = false;
123123
this._helpFlags = '-h, --help';
@@ -937,7 +937,7 @@ class Command extends EventEmitter {
937937
*/
938938
_findCommand(name) {
939939
if (!name) return undefined;
940-
return this.commands.find(cmd => cmd._name === name || cmd._alias === name);
940+
return this.commands.find(cmd => cmd._name === name || cmd._aliases.includes(name));
941941
};
942942

943943
/**
@@ -1201,7 +1201,7 @@ class Command extends EventEmitter {
12011201
*
12021202
* @param {string} str
12031203
* @param {Object} [argsDescription]
1204-
* @return {String|Command}
1204+
* @return {string|Command}
12051205
* @api public
12061206
*/
12071207

@@ -1213,15 +1213,17 @@ class Command extends EventEmitter {
12131213
};
12141214

12151215
/**
1216-
* Set an alias for the command
1216+
* Set an alias for the command.
12171217
*
1218-
* @param {string} alias
1219-
* @return {String|Command}
1218+
* You may call more than once to add multiple aliases. Only the first alias is shown in the auto-generated help.
1219+
*
1220+
* @param {string} [alias]
1221+
* @return {string|Command}
12201222
* @api public
12211223
*/
12221224

12231225
alias(alias) {
1224-
if (alias === undefined) return this._alias;
1226+
if (alias === undefined) return this._aliases[0]; // just return first, for backwards compatibility
12251227

12261228
let command = this;
12271229
if (this.commands.length !== 0 && this.commands[this.commands.length - 1]._executableHandler) {
@@ -1231,7 +1233,25 @@ class Command extends EventEmitter {
12311233

12321234
if (alias === command._name) throw new Error('Command alias can\'t be the same as its name');
12331235

1234-
command._alias = alias;
1236+
command._aliases.push(alias);
1237+
return this;
1238+
};
1239+
1240+
/**
1241+
* Set aliases for the command.
1242+
*
1243+
* Only the first alias is shown in the auto-generated help.
1244+
*
1245+
* @param {string[]} [aliases]
1246+
* @return {string[]|Command}
1247+
* @api public
1248+
*/
1249+
1250+
aliases(aliases) {
1251+
// Getter for the array of aliases is the main reason for having aliases() in addition to alias().
1252+
if (aliases === undefined) return this._aliases;
1253+
1254+
aliases.forEach((alias) => this.alias(alias));
12351255
return this;
12361256
};
12371257

@@ -1290,7 +1310,7 @@ class Command extends EventEmitter {
12901310

12911311
return [
12921312
cmd._name +
1293-
(cmd._alias ? '|' + cmd._alias : '') +
1313+
(cmd._aliases[0] ? '|' + cmd._aliases[0] : '') +
12941314
(cmd.options.length ? ' [options]' : '') +
12951315
(args ? ' ' + args : ''),
12961316
cmd._description
@@ -1450,8 +1470,8 @@ class Command extends EventEmitter {
14501470
}
14511471

14521472
let cmdName = this._name;
1453-
if (this._alias) {
1454-
cmdName = cmdName + '|' + this._alias;
1473+
if (this._aliases[0]) {
1474+
cmdName = cmdName + '|' + this._aliases[0];
14551475
}
14561476
let parentCmdNames = '';
14571477
for (let parentCmd = this.parent; parentCmd; parentCmd = parentCmd.parent) {

tests/command.alias.test.js

+54-1
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,64 @@ test('when command has alias then appears in help', () => {
1212
expect(helpInformation).toMatch('info|i');
1313
});
1414

15-
test('when command = alias then error', () => {
15+
test('when command has aliases added separately then only first appears in help', () => {
16+
const program = new commander.Command();
17+
program
18+
.command('list [thing]')
19+
.alias('ls')
20+
.alias('dir');
21+
const helpInformation = program.helpInformation();
22+
expect(helpInformation).toMatch('list|ls ');
23+
});
24+
25+
test('when command has aliases then only first appears in help', () => {
26+
const program = new commander.Command();
27+
program
28+
.command('list [thing]')
29+
.aliases(['ls', 'dir']);
30+
const helpInformation = program.helpInformation();
31+
expect(helpInformation).toMatch('list|ls ');
32+
});
33+
34+
test('when command name = alias then error', () => {
1635
const program = new commander.Command();
1736
expect(() => {
1837
program
1938
.command('fail')
2039
.alias('fail');
2140
}).toThrow("Command alias can't be the same as its name");
2241
});
42+
43+
test('when use alias then action handler called', () => {
44+
const program = new commander.Command();
45+
const actionMock = jest.fn();
46+
program
47+
.command('list')
48+
.alias('ls')
49+
.action(actionMock);
50+
program.parse(['ls'], { from: 'user' });
51+
expect(actionMock).toHaveBeenCalled();
52+
});
53+
54+
test('when use second alias added separately then action handler called', () => {
55+
const program = new commander.Command();
56+
const actionMock = jest.fn();
57+
program
58+
.command('list')
59+
.alias('ls')
60+
.alias('dir')
61+
.action(actionMock);
62+
program.parse(['dir'], { from: 'user' });
63+
expect(actionMock).toHaveBeenCalled();
64+
});
65+
66+
test('when use second of aliases then action handler called', () => {
67+
const program = new commander.Command();
68+
const actionMock = jest.fn();
69+
program
70+
.command('list')
71+
.aliases(['ls', 'dir'])
72+
.action(actionMock);
73+
program.parse(['dir'], { from: 'user' });
74+
expect(actionMock).toHaveBeenCalled();
75+
});

typings/commander-tests.ts

+4
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,10 @@ const descriptionValue: string = program.description();
169169
const aliasThis: commander.Command = program.alias('my alias');
170170
const aliasValue: string = program.alias();
171171

172+
// aliases
173+
const aliasesThis: commander.Command = program.aliases(['first-alias', 'second-alias']);
174+
const aliasesValue: string[] = program.aliases();
175+
172176
// usage
173177
const usageThis: commander.Command = program.usage('my usage');
174178
const usageValue: string = program.usage();

typings/index.d.ts

+15
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,8 @@ declare namespace commander {
275275
/**
276276
* Set an alias for the command.
277277
*
278+
* You may call more than once to add multiple aliases. Only the first alias is shown in the auto-generated help.
279+
*
278280
* @returns `this` command for chaining
279281
*/
280282
alias(alias: string): this;
@@ -283,6 +285,19 @@ declare namespace commander {
283285
*/
284286
alias(): string;
285287

288+
/**
289+
* Set aliases for the command.
290+
*
291+
* Only the first alias is shown in the auto-generated help.
292+
*
293+
* @returns `this` command for chaining
294+
*/
295+
aliases(aliases: string[]): this;
296+
/**
297+
* Get aliases for the command.
298+
*/
299+
aliases(): string[];
300+
286301
/**
287302
* Set the command usage.
288303
*

0 commit comments

Comments
 (0)