Skip to content

Commit b5d95ee

Browse files
authored
Add opts to addCommand, and rename noHelp to hidden (#1232)
1 parent 8ec3e7f commit b5d95ee

File tree

7 files changed

+74
-13
lines changed

7 files changed

+74
-13
lines changed

Readme.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,7 @@ program
330330
.addCommand(build.makeBuildCommand());
331331
```
332332
333-
Configuration options can be passed with the call to `.command()`. Specifying `true` for `opts.noHelp` will remove the command from the generated help output. Specifying `true` for `opts.isDefault` will run the subcommand if no other subcommand is specified ([example](./examples/defaultCommand.js)).
333+
Configuration options can be passed with the call to `.command()` and `.addCommand()`. Specifying `true` for `opts.hidden` will remove the command from the generated help output. Specifying `true` for `opts.isDefault` will run the subcommand if no other subcommand is specified ([example](./examples/defaultCommand.js)).
334334
335335
### Specify the argument syntax
336336

index.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ class Command extends EventEmitter {
119119
this._exitCallback = null;
120120
this._alias = null;
121121

122-
this._noHelp = false;
122+
this._hidden = false;
123123
this._helpFlags = '-h, --help';
124124
this._helpDescription = 'display help for command';
125125
this._helpShortFlag = '-h';
@@ -174,7 +174,7 @@ class Command extends EventEmitter {
174174
}
175175
if (opts.isDefault) this._defaultCommandName = cmd._name;
176176

177-
cmd._noHelp = !!opts.noHelp;
177+
cmd._hidden = !!(opts.noHelp || opts.hidden);
178178
cmd._helpFlags = this._helpFlags;
179179
cmd._helpDescription = this._helpDescription;
180180
cmd._helpShortFlag = this._helpShortFlag;
@@ -216,11 +216,12 @@ class Command extends EventEmitter {
216216
* See .command() for creating an attached subcommand which inherits settings from its parent.
217217
*
218218
* @param {Command} cmd - new subcommand
219+
* @param {Object} [opts] - configuration options
219220
* @return {Command} `this` command for chaining
220221
* @api public
221222
*/
222223

223-
addCommand(cmd) {
224+
addCommand(cmd, opts) {
224225
if (!cmd._name) throw new Error('Command passed to .addCommand() must have a name');
225226

226227
// To keep things simple, block automatic name generation for deeply nested executables.
@@ -235,6 +236,10 @@ class Command extends EventEmitter {
235236
}
236237
checkExplicitNames(cmd.commands);
237238

239+
opts = opts || {};
240+
if (opts.isDefault) this._defaultCommandName = cmd._name;
241+
if (opts.noHelp || opts.hidden) cmd._hidden = true; // modifying passed command due to existing implementation
242+
238243
this.commands.push(cmd);
239244
cmd.parent = this;
240245
return this;
@@ -1275,7 +1280,7 @@ class Command extends EventEmitter {
12751280

12761281
prepareCommands() {
12771282
const commandDetails = this.commands.filter((cmd) => {
1278-
return !cmd._noHelp;
1283+
return !cmd._hidden;
12791284
}).map((cmd) => {
12801285
const args = cmd._args.map((arg) => {
12811286
return humanReadableArgName(arg);

tests/command.default.test.js

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,47 @@ describe('default action command', () => {
4444
expect(actionMock).toHaveBeenCalled();
4545
});
4646

47-
test('when default subcommand and unrecognised argument then call default with argument', () => {
47+
test('when default subcommand and unrecognised argument then call default', () => {
4848
const { program, actionMock } = makeProgram();
4949
program.parse('node test.js an-argument'.split(' '));
5050
expect(actionMock).toHaveBeenCalled();
5151
});
5252

53-
test('when default subcommand and unrecognised option then call default with option', () => {
53+
test('when default subcommand and unrecognised option then call default', () => {
54+
const { program, actionMock } = makeProgram();
55+
program.parse('node test.js --an-option'.split(' '));
56+
expect(actionMock).toHaveBeenCalled();
57+
});
58+
});
59+
60+
describe('default added command', () => {
61+
function makeProgram() {
62+
const actionMock = jest.fn();
63+
const defaultCmd = new commander.Command('default')
64+
.allowUnknownOption()
65+
.action(actionMock);
66+
67+
const program = new commander.Command();
68+
program
69+
.command('other');
70+
program
71+
.addCommand(defaultCmd, { isDefault: true });
72+
return { program, actionMock };
73+
}
74+
75+
test('when default subcommand and no command then call default', () => {
76+
const { program, actionMock } = makeProgram();
77+
program.parse('node test.js'.split(' '));
78+
expect(actionMock).toHaveBeenCalled();
79+
});
80+
81+
test('when default subcommand and unrecognised argument then call default', () => {
82+
const { program, actionMock } = makeProgram();
83+
program.parse('node test.js an-argument'.split(' '));
84+
expect(actionMock).toHaveBeenCalled();
85+
});
86+
87+
test('when default subcommand and unrecognised option then call default', () => {
5488
const { program, actionMock } = makeProgram();
5589
program.parse('node test.js --an-option'.split(' '));
5690
expect(actionMock).toHaveBeenCalled();

tests/command.help.test.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,29 @@ test('when call outputHelp(cb) then display cb output', () => {
8888
writeSpy.mockClear();
8989
});
9090

91+
// noHelp is now named hidden, not officially deprecated yet
9192
test('when command sets noHelp then not displayed in helpInformation', () => {
9293
const program = new commander.Command();
9394
program
9495
.command('secret', 'secret description', { noHelp: true });
9596
const helpInformation = program.helpInformation();
9697
expect(helpInformation).not.toMatch('secret');
9798
});
99+
100+
test('when command sets hidden then not displayed in helpInformation', () => {
101+
const program = new commander.Command();
102+
program
103+
.command('secret', 'secret description', { hidden: true });
104+
const helpInformation = program.helpInformation();
105+
expect(helpInformation).not.toMatch('secret');
106+
});
107+
108+
test('when addCommand with hidden:true then not displayed in helpInformation', () => {
109+
const secretCmd = new commander.Command('secret');
110+
111+
const program = new commander.Command();
112+
program
113+
.addCommand(secretCmd, { hidden: true });
114+
const helpInformation = program.helpInformation();
115+
expect(helpInformation).not.toMatch('secret');
116+
});

tests/fixtures/pm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ program
1111
.command('list', 'list packages installed').alias('lst')
1212
.command('listen', 'listen for supported signal events').alias('l')
1313
.command('publish', 'publish or update package').alias('p')
14-
.command('default', 'default command', { noHelp: true, isDefault: true })
14+
.command('default', 'default command', { hidden: true, isDefault: true })
1515
.command('specifyInstall', 'specify install subcommand', { executableFile: 'pm-install' })
1616
.command('specifyPublish', 'specify publish subcommand', { executableFile: 'pm-publish' })
1717
.command('silent', 'silently succeed')

typings/commander-tests.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ const versionThis3: commander.Command = program.version('1.2.3', '-r,--revision'
4545

4646
// command (and CommandOptions)
4747
const commandNew1: commander.Command = program.command('action');
48-
const commandNew2: commander.Command = program.command('action', { isDefault: true, noHelp: true });
48+
const commandNew2: commander.Command = program.command('action', { isDefault: true, hidden: true, noHelp: true });
4949
const commandThis1: commander.Command = program.command('exec', 'exec description');
50-
const commandThis2: commander.Command = program.command('exec', 'exec description', { isDefault: true, noHelp: true, executableFile: 'foo' });
50+
const commandThis2: commander.Command = program.command('exec', 'exec description', { isDefault: true, hidden: true, noHelp: true, executableFile: 'foo' });
5151

5252
// addCommand
5353
const addCommandThis: commander.Command = program.addCommand(new commander.Command('abc'));

typings/index.d.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ declare namespace commander {
8383
* @param opts - configuration options
8484
* @returns `this` command for chaining
8585
*/
86-
command(nameAndArgs: string, description: string, opts?: commander.CommandOptions): this;
86+
command(nameAndArgs: string, description: string, opts?: commander.ExecutableCommandOptions): this;
8787

8888
/**
8989
* Factory routine to create a new unattached command.
@@ -100,7 +100,7 @@ declare namespace commander {
100100
*
101101
* @returns `this` command for chaining
102102
*/
103-
addCommand(cmd: Command): this;
103+
addCommand(cmd: Command, opts?: CommandOptions): this;
104104

105105
/**
106106
* Define argument syntax for command.
@@ -343,8 +343,11 @@ declare namespace commander {
343343
type CommandConstructor = new (name?: string) => Command;
344344

345345
interface CommandOptions {
346-
noHelp?: boolean;
346+
noHelp?: boolean; // old name for hidden
347+
hidden?: boolean;
347348
isDefault?: boolean;
349+
}
350+
interface ExecutableCommandOptions extends CommandOptions {
348351
executableFile?: string;
349352
}
350353

0 commit comments

Comments
 (0)