Skip to content

Commit 5517d25

Browse files
authored
Add copyInheritedSettings (#1557)
* Factor out copySettings so can be used in programs using addCommand. * Fill out tests for properties for copySettings * Add TypeScript * Rename
1 parent 80054ba commit 5517d25

File tree

5 files changed

+181
-20
lines changed

5 files changed

+181
-20
lines changed

lib/command.js

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,35 @@ class Command extends EventEmitter {
7373
this._helpConfiguration = {};
7474
}
7575

76+
/**
77+
* Copy settings that are useful to have in common across root command and subcommands.
78+
*
79+
* (Used internally when adding a command using `.command()` so subcommands inherit parent settings.)
80+
*
81+
* @param {Command} sourceCommand
82+
* @return {Command} returns `this` for executable command
83+
*/
84+
copyInheritedSettings(sourceCommand) {
85+
this._outputConfiguration = sourceCommand._outputConfiguration;
86+
this._hasHelpOption = sourceCommand._hasHelpOption;
87+
this._helpFlags = sourceCommand._helpFlags;
88+
this._helpDescription = sourceCommand._helpDescription;
89+
this._helpShortFlag = sourceCommand._helpShortFlag;
90+
this._helpLongFlag = sourceCommand._helpLongFlag;
91+
this._helpCommandName = sourceCommand._helpCommandName;
92+
this._helpCommandnameAndArgs = sourceCommand._helpCommandnameAndArgs;
93+
this._helpCommandDescription = sourceCommand._helpCommandDescription;
94+
this._helpConfiguration = sourceCommand._helpConfiguration;
95+
this._exitCallback = sourceCommand._exitCallback;
96+
this._storeOptionsAsProperties = sourceCommand._storeOptionsAsProperties;
97+
this._combineFlagAndOptionalValue = sourceCommand._combineFlagAndOptionalValue;
98+
this._allowExcessArguments = sourceCommand._allowExcessArguments;
99+
this._enablePositionalOptions = sourceCommand._enablePositionalOptions;
100+
this._showHelpAfterError = sourceCommand._showHelpAfterError;
101+
102+
return this;
103+
}
104+
76105
/**
77106
* Define a command.
78107
*
@@ -108,37 +137,19 @@ class Command extends EventEmitter {
108137
}
109138
opts = opts || {};
110139
const [, name, args] = nameAndArgs.match(/([^ ]+) *(.*)/);
111-
const cmd = this.createCommand(name);
112140

141+
const cmd = this.createCommand(name);
113142
if (desc) {
114143
cmd.description(desc);
115144
cmd._executableHandler = true;
116145
}
117146
if (opts.isDefault) this._defaultCommandName = cmd._name;
118-
119-
cmd._outputConfiguration = this._outputConfiguration;
120-
121147
cmd._hidden = !!(opts.noHelp || opts.hidden); // noHelp is deprecated old name for hidden
122-
cmd._hasHelpOption = this._hasHelpOption;
123-
cmd._helpFlags = this._helpFlags;
124-
cmd._helpDescription = this._helpDescription;
125-
cmd._helpShortFlag = this._helpShortFlag;
126-
cmd._helpLongFlag = this._helpLongFlag;
127-
cmd._helpCommandName = this._helpCommandName;
128-
cmd._helpCommandnameAndArgs = this._helpCommandnameAndArgs;
129-
cmd._helpCommandDescription = this._helpCommandDescription;
130-
cmd._helpConfiguration = this._helpConfiguration;
131-
cmd._exitCallback = this._exitCallback;
132-
cmd._storeOptionsAsProperties = this._storeOptionsAsProperties;
133-
cmd._combineFlagAndOptionalValue = this._combineFlagAndOptionalValue;
134-
cmd._allowExcessArguments = this._allowExcessArguments;
135-
cmd._enablePositionalOptions = this._enablePositionalOptions;
136-
cmd._showHelpAfterError = this._showHelpAfterError;
137-
138148
cmd._executableFile = opts.executableFile || null; // Custom name for executable file, set missing to null to match constructor
139149
if (args) cmd.arguments(args);
140150
this.commands.push(cmd);
141151
cmd.parent = this;
152+
cmd.copyInheritedSettings(this);
142153

143154
if (desc) return this;
144155
return cmd;

tests/command.chain.test.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,4 +183,11 @@ describe('Command methods that should return this for chaining', () => {
183183
const result = program.showHelpAfterError();
184184
expect(result).toBe(program);
185185
});
186+
187+
test('when call .copyInheritedSettings() then returns this', () => {
188+
const program = new Command();
189+
const cmd = new Command();
190+
const result = cmd.copyInheritedSettings(program);
191+
expect(result).toBe(cmd);
192+
});
186193
});

tests/command.copySettings.test.js

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
const commander = require('../');
2+
3+
// Tests some private properties as simpler than pure tests of observable behaviours.
4+
// Testing before and after values in some cases, to ensure value actually changes (when copied).
5+
6+
test('when add subcommand with .command() then calls copyInheritedSettings from parent', () => {
7+
const program = new commander.Command();
8+
9+
// This is a bit intrusive, but check expectation that copyInheritedSettings is called internally.
10+
const copySettingMock = jest.fn();
11+
program.createCommand = (name) => {
12+
const cmd = new commander.Command(name);
13+
cmd.copyInheritedSettings = copySettingMock;
14+
return cmd;
15+
};
16+
program.command('sub');
17+
18+
expect(copySettingMock).toHaveBeenCalledWith(program);
19+
});
20+
21+
describe('copyInheritedSettings property tests', () => {
22+
test('when copyInheritedSettings then copies outputConfiguration(config)', () => {
23+
const source = new commander.Command();
24+
const cmd = new commander.Command();
25+
26+
source.configureOutput({ foo: 'bar' });
27+
cmd.copyInheritedSettings(source);
28+
expect(cmd.configureOutput().foo).toEqual('bar');
29+
});
30+
31+
test('when copyInheritedSettings then copies helpOption(false)', () => {
32+
const source = new commander.Command();
33+
const cmd = new commander.Command();
34+
expect(cmd._hasHelpOption).toBeTruthy();
35+
36+
source.helpOption(false);
37+
cmd.copyInheritedSettings(source);
38+
expect(cmd._hasHelpOption).toBeFalsy();
39+
});
40+
41+
test('when copyInheritedSettings then copies helpOption(flags, description)', () => {
42+
const source = new commander.Command();
43+
const cmd = new commander.Command();
44+
45+
source.helpOption('-Z, --zz', 'ddd');
46+
cmd.copyInheritedSettings(source);
47+
expect(cmd._helpFlags).toBe('-Z, --zz');
48+
expect(cmd._helpDescription).toBe('ddd');
49+
expect(cmd._helpShortFlag).toBe('-Z');
50+
expect(cmd._helpLongFlag).toBe('--zz');
51+
});
52+
53+
test('when copyInheritedSettings then copies addHelpCommand(name, description)', () => {
54+
const source = new commander.Command();
55+
const cmd = new commander.Command();
56+
57+
source.addHelpCommand('HELP [cmd]', 'ddd');
58+
cmd.copyInheritedSettings(source);
59+
expect(cmd._helpCommandName).toBe('HELP');
60+
expect(cmd._helpCommandnameAndArgs).toBe('HELP [cmd]');
61+
expect(cmd._helpCommandDescription).toBe('ddd');
62+
});
63+
64+
test('when copyInheritedSettings then copies configureHelp(config)', () => {
65+
const source = new commander.Command();
66+
const cmd = new commander.Command();
67+
68+
const configuration = { foo: 'bar', helpWidth: 123, sortSubcommands: true };
69+
source.configureHelp(configuration);
70+
cmd.copyInheritedSettings(source);
71+
expect(cmd.configureHelp()).toEqual(configuration);
72+
});
73+
74+
test('when copyInheritedSettings then copies exitOverride()', () => {
75+
const source = new commander.Command();
76+
const cmd = new commander.Command();
77+
78+
expect(cmd._exitCallback).toBeFalsy();
79+
source.exitOverride();
80+
cmd.copyInheritedSettings(source);
81+
expect(cmd._exitCallback).toBeTruthy(); // actually a function
82+
});
83+
84+
test('when copyInheritedSettings then copies storeOptionsAsProperties()', () => {
85+
const source = new commander.Command();
86+
const cmd = new commander.Command();
87+
88+
expect(cmd._storeOptionsAsProperties).toBeFalsy();
89+
source.storeOptionsAsProperties();
90+
cmd.copyInheritedSettings(source);
91+
expect(cmd._storeOptionsAsProperties).toBeTruthy();
92+
});
93+
94+
test('when copyInheritedSettings then copies combineFlagAndOptionalValue()', () => {
95+
const source = new commander.Command();
96+
const cmd = new commander.Command();
97+
98+
expect(cmd._combineFlagAndOptionalValue).toBeTruthy();
99+
source.combineFlagAndOptionalValue(false);
100+
cmd.copyInheritedSettings(source);
101+
expect(cmd._combineFlagAndOptionalValue).toBeFalsy();
102+
});
103+
104+
test('when copyInheritedSettings then copies allowExcessArguments()', () => {
105+
const source = new commander.Command();
106+
const cmd = new commander.Command();
107+
108+
expect(cmd._allowExcessArguments).toBeTruthy();
109+
source.allowExcessArguments(false);
110+
cmd.copyInheritedSettings(source);
111+
expect(cmd._allowExcessArguments).toBeFalsy();
112+
});
113+
114+
test('when copyInheritedSettings then copies enablePositionalOptions()', () => {
115+
const source = new commander.Command();
116+
const cmd = new commander.Command();
117+
118+
expect(cmd._enablePositionalOptions).toBeFalsy();
119+
source.enablePositionalOptions();
120+
cmd.copyInheritedSettings(source);
121+
expect(cmd._enablePositionalOptions).toBeTruthy();
122+
});
123+
124+
test('when copyInheritedSettings then copies showHelpAfterError()', () => {
125+
const source = new commander.Command();
126+
const cmd = new commander.Command();
127+
128+
expect(cmd._showHelpAfterError).toBeFalsy();
129+
source.showHelpAfterError();
130+
cmd.copyInheritedSettings(source);
131+
expect(cmd._showHelpAfterError).toBeTruthy();
132+
});
133+
});

typings/index.d.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,13 @@ export class Command {
389389
/** Get configuration */
390390
configureOutput(): OutputConfiguration;
391391

392+
/**
393+
* Copy settings that are useful to have in common across root command and subcommands.
394+
*
395+
* (Used internally when adding a command using `.command()` so subcommands inherit parent settings.)
396+
*/
397+
copyInheritedSettings(sourceCommand: Command): this;
398+
392399
/**
393400
* Display the help or a custom message after an error occurs.
394401
*/

typings/index.test-d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,9 @@ expectType<commander.Command>(program.configureHelp({
285285
}));
286286
expectType<commander.HelpConfiguration>(program.configureHelp());
287287

288+
// copyInheritedSettings
289+
expectType<commander.Command>(program.copyInheritedSettings(new commander.Command()));
290+
288291
// showHelpAfterError
289292
expectType<commander.Command>(program.showHelpAfterError());
290293
expectType<commander.Command>(program.showHelpAfterError(true));

0 commit comments

Comments
 (0)