Skip to content

Commit

Permalink
Add support for multiple aliases (#1236)
Browse files Browse the repository at this point in the history
* Allow for multiple aliases #531

* Add tests for multiple alias behaviours

* Add aliases() mainly for getter

* Fix typo
  • Loading branch information
shadowspawn committed Apr 21, 2020
1 parent b59adfc commit 28e8d3f
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 12 deletions.
42 changes: 31 additions & 11 deletions index.js
Expand Up @@ -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';
Expand Down Expand Up @@ -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));
};

/**
Expand Down Expand Up @@ -1201,7 +1201,7 @@ class Command extends EventEmitter {
*
* @param {string} str
* @param {Object} [argsDescription]
* @return {String|Command}
* @return {string|Command}
* @api public
*/

Expand All @@ -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) {
Expand All @@ -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;
};

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand Down
55 changes: 54 additions & 1 deletion tests/command.alias.test.js
Expand Up @@ -12,11 +12,64 @@ 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
.command('fail')
.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();
});
4 changes: 4 additions & 0 deletions typings/commander-tests.ts
Expand Up @@ -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();
Expand Down
15 changes: 15 additions & 0 deletions typings/index.d.ts
Expand Up @@ -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;
Expand All @@ -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.
*
Expand Down

0 comments on commit 28e8d3f

Please sign in to comment.