Skip to content

Commit f17ecbd

Browse files
authored
Allow readonly array as parameter to .aliases(), .parse(), .parseAsync() (#1669)
* Make .aliases() param readonly * Make .parse() and .parseAsync() param readonly in TypeScript
1 parent 4b9fee9 commit f17ecbd

File tree

4 files changed

+100
-3
lines changed

4 files changed

+100
-3
lines changed

tests/command.alias.test.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,29 @@ test('when set alias on executable then can get alias', () => {
9696
.alias(alias);
9797
expect(program.commands[0].alias()).toEqual(alias);
9898
});
99+
100+
describe('aliases parameter is treated as readonly, per TypeScript declaration', () => {
101+
test('when aliases called then parameter does not change', () => {
102+
// Unlikely this could break, but check the API we are declaring in TypeScript.
103+
const original = ['b', 'bld'];
104+
const param = original.slice();
105+
new commander.Command('build').aliases(param);
106+
expect(param).toEqual(original);
107+
});
108+
109+
test('when aliases called and aliases later changed then parameter does not change', () => {
110+
const original = ['b', 'bld'];
111+
const param = original.slice();
112+
const cmd = new commander.Command('build').aliases(param);
113+
cmd.alias('BBB');
114+
expect(param).toEqual(original);
115+
});
116+
117+
test('when aliases called and parameter later changed then aliases does not change', () => {
118+
const original = ['b', 'bld'];
119+
const param = original.slice();
120+
const cmd = new commander.Command('build').aliases(param);
121+
param.length = 0;
122+
expect(cmd.aliases()).toEqual(original);
123+
});
124+
});

tests/command.parse.test.js

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,71 @@ test('when parse strings instead of array then throw', () => {
9696
program.parse('node', 'test');
9797
}).toThrow();
9898
});
99+
100+
describe('parse parameter is treated as readonly, per TypeScript declaration', () => {
101+
test('when parse called then parameter does not change', () => {
102+
const program = new commander.Command();
103+
program.option('--debug');
104+
const original = ['node', '--debug', 'arg'];
105+
const param = original.slice();
106+
program.parse(param);
107+
expect(param).toEqual(original);
108+
});
109+
110+
test('when parse called and parsed args later changed then parameter does not change', () => {
111+
const program = new commander.Command();
112+
program.option('--debug');
113+
const original = ['node', '--debug', 'arg'];
114+
const param = original.slice();
115+
program.parse(param);
116+
program.args.length = 0;
117+
program.rawArgs.length = 0;
118+
expect(param).toEqual(original);
119+
});
120+
121+
test('when parse called and param later changed then parsed args do not change', () => {
122+
const program = new commander.Command();
123+
program.option('--debug');
124+
const param = ['node', '--debug', 'arg'];
125+
program.parse(param);
126+
const oldArgs = program.args.slice();
127+
const oldRawArgs = program.rawArgs.slice();
128+
param.length = 0;
129+
expect(program.args).toEqual(oldArgs);
130+
expect(program.rawArgs).toEqual(oldRawArgs);
131+
});
132+
});
133+
134+
describe('parseAsync parameter is treated as readonly, per TypeScript declaration', () => {
135+
test('when parse called then parameter does not change', async() => {
136+
const program = new commander.Command();
137+
program.option('--debug');
138+
const original = ['node', '--debug', 'arg'];
139+
const param = original.slice();
140+
await program.parseAsync(param);
141+
expect(param).toEqual(original);
142+
});
143+
144+
test('when parseAsync called and parsed args later changed then parameter does not change', async() => {
145+
const program = new commander.Command();
146+
program.option('--debug');
147+
const original = ['node', '--debug', 'arg'];
148+
const param = original.slice();
149+
await program.parseAsync(param);
150+
program.args.length = 0;
151+
program.rawArgs.length = 0;
152+
expect(param).toEqual(original);
153+
});
154+
155+
test('when parseAsync called and param later changed then parsed args do not change', async() => {
156+
const program = new commander.Command();
157+
program.option('--debug');
158+
const param = ['node', '--debug', 'arg'];
159+
await program.parseAsync(param);
160+
const oldArgs = program.args.slice();
161+
const oldRawArgs = program.rawArgs.slice();
162+
param.length = 0;
163+
expect(program.args).toEqual(oldArgs);
164+
expect(program.rawArgs).toEqual(oldRawArgs);
165+
});
166+
});

typings/index.d.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -626,7 +626,7 @@ export class Command {
626626
*
627627
* @returns `this` command for chaining
628628
*/
629-
parse(argv?: string[], options?: ParseOptions): this;
629+
parse(argv?: readonly string[], options?: ParseOptions): this;
630630

631631
/**
632632
* Parse `argv`, setting options and invoking commands when defined.
@@ -645,7 +645,7 @@ export class Command {
645645
*
646646
* @returns Promise
647647
*/
648-
parseAsync(argv?: string[], options?: ParseOptions): Promise<this>;
648+
parseAsync(argv?: readonly string[], options?: ParseOptions): Promise<this>;
649649

650650
/**
651651
* Parse options from `argv` removing known options,
@@ -698,7 +698,7 @@ export class Command {
698698
*
699699
* @returns `this` command for chaining
700700
*/
701-
aliases(aliases: string[]): this;
701+
aliases(aliases: readonly string[]): this;
702702
/**
703703
* Get aliases for the command.
704704
*/

typings/index.test-d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,13 +187,15 @@ expectType<commander.Command>(program.parse(process.argv));
187187
expectType<commander.Command>(program.parse(['node', 'script.js'], { from: 'node' }));
188188
expectType<commander.Command>(program.parse(['node', 'script.js'], { from: 'electron' }));
189189
expectType<commander.Command>(program.parse(['--option'], { from: 'user' }));
190+
expectType<commander.Command>(program.parse(['node', 'script.js'] as const));
190191

191192
// parseAsync, same tests as parse
192193
expectType<Promise<commander.Command>>(program.parseAsync());
193194
expectType<Promise<commander.Command>>(program.parseAsync(process.argv));
194195
expectType<Promise<commander.Command>>(program.parseAsync(['node', 'script.js'], { from: 'node' }));
195196
expectType<Promise<commander.Command>>(program.parseAsync(['node', 'script.js'], { from: 'electron' }));
196197
expectType<Promise<commander.Command>>(program.parseAsync(['--option'], { from: 'user' }));
198+
expectType<Promise<commander.Command>>(program.parseAsync(['node', 'script.js'] as const));
197199

198200
// parseOptions (and ParseOptionsResult)
199201
expectType<{operands: string[]; unknown: string[]}>(program.parseOptions(['node', 'script.js', 'hello']));
@@ -224,6 +226,7 @@ expectType<string>(program.alias());
224226

225227
// aliases
226228
expectType<commander.Command>(program.aliases(['first-alias', 'second-alias']));
229+
expectType<commander.Command>(program.aliases(['first-alias', 'second-alias'] as const));
227230
expectType<string[]>(program.aliases());
228231

229232
// usage

0 commit comments

Comments
 (0)