Skip to content

Commit

Permalink
Allow readonly array as parameter to .aliases(), .parse(), .parseAsyn…
Browse files Browse the repository at this point in the history
…c() (#1669)

* Make .aliases() param readonly

* Make .parse() and .parseAsync() param readonly in TypeScript
  • Loading branch information
shadowspawn committed Jan 3, 2022
1 parent 4b9fee9 commit f17ecbd
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 3 deletions.
26 changes: 26 additions & 0 deletions tests/command.alias.test.js
Expand Up @@ -96,3 +96,29 @@ test('when set alias on executable then can get alias', () => {
.alias(alias);
expect(program.commands[0].alias()).toEqual(alias);
});

describe('aliases parameter is treated as readonly, per TypeScript declaration', () => {
test('when aliases called then parameter does not change', () => {
// Unlikely this could break, but check the API we are declaring in TypeScript.
const original = ['b', 'bld'];
const param = original.slice();
new commander.Command('build').aliases(param);
expect(param).toEqual(original);
});

test('when aliases called and aliases later changed then parameter does not change', () => {
const original = ['b', 'bld'];
const param = original.slice();
const cmd = new commander.Command('build').aliases(param);
cmd.alias('BBB');
expect(param).toEqual(original);
});

test('when aliases called and parameter later changed then aliases does not change', () => {
const original = ['b', 'bld'];
const param = original.slice();
const cmd = new commander.Command('build').aliases(param);
param.length = 0;
expect(cmd.aliases()).toEqual(original);
});
});
68 changes: 68 additions & 0 deletions tests/command.parse.test.js
Expand Up @@ -96,3 +96,71 @@ test('when parse strings instead of array then throw', () => {
program.parse('node', 'test');
}).toThrow();
});

describe('parse parameter is treated as readonly, per TypeScript declaration', () => {
test('when parse called then parameter does not change', () => {
const program = new commander.Command();
program.option('--debug');
const original = ['node', '--debug', 'arg'];
const param = original.slice();
program.parse(param);
expect(param).toEqual(original);
});

test('when parse called and parsed args later changed then parameter does not change', () => {
const program = new commander.Command();
program.option('--debug');
const original = ['node', '--debug', 'arg'];
const param = original.slice();
program.parse(param);
program.args.length = 0;
program.rawArgs.length = 0;
expect(param).toEqual(original);
});

test('when parse called and param later changed then parsed args do not change', () => {
const program = new commander.Command();
program.option('--debug');
const param = ['node', '--debug', 'arg'];
program.parse(param);
const oldArgs = program.args.slice();
const oldRawArgs = program.rawArgs.slice();
param.length = 0;
expect(program.args).toEqual(oldArgs);
expect(program.rawArgs).toEqual(oldRawArgs);
});
});

describe('parseAsync parameter is treated as readonly, per TypeScript declaration', () => {
test('when parse called then parameter does not change', async() => {
const program = new commander.Command();
program.option('--debug');
const original = ['node', '--debug', 'arg'];
const param = original.slice();
await program.parseAsync(param);
expect(param).toEqual(original);
});

test('when parseAsync called and parsed args later changed then parameter does not change', async() => {
const program = new commander.Command();
program.option('--debug');
const original = ['node', '--debug', 'arg'];
const param = original.slice();
await program.parseAsync(param);
program.args.length = 0;
program.rawArgs.length = 0;
expect(param).toEqual(original);
});

test('when parseAsync called and param later changed then parsed args do not change', async() => {
const program = new commander.Command();
program.option('--debug');
const param = ['node', '--debug', 'arg'];
await program.parseAsync(param);
const oldArgs = program.args.slice();
const oldRawArgs = program.rawArgs.slice();
param.length = 0;
expect(program.args).toEqual(oldArgs);
expect(program.rawArgs).toEqual(oldRawArgs);
});
});
6 changes: 3 additions & 3 deletions typings/index.d.ts
Expand Up @@ -626,7 +626,7 @@ export class Command {
*
* @returns `this` command for chaining
*/
parse(argv?: string[], options?: ParseOptions): this;
parse(argv?: readonly string[], options?: ParseOptions): this;

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

/**
* Parse options from `argv` removing known options,
Expand Down Expand Up @@ -698,7 +698,7 @@ export class Command {
*
* @returns `this` command for chaining
*/
aliases(aliases: string[]): this;
aliases(aliases: readonly string[]): this;
/**
* Get aliases for the command.
*/
Expand Down
3 changes: 3 additions & 0 deletions typings/index.test-d.ts
Expand Up @@ -187,13 +187,15 @@ expectType<commander.Command>(program.parse(process.argv));
expectType<commander.Command>(program.parse(['node', 'script.js'], { from: 'node' }));
expectType<commander.Command>(program.parse(['node', 'script.js'], { from: 'electron' }));
expectType<commander.Command>(program.parse(['--option'], { from: 'user' }));
expectType<commander.Command>(program.parse(['node', 'script.js'] as const));

// parseAsync, same tests as parse
expectType<Promise<commander.Command>>(program.parseAsync());
expectType<Promise<commander.Command>>(program.parseAsync(process.argv));
expectType<Promise<commander.Command>>(program.parseAsync(['node', 'script.js'], { from: 'node' }));
expectType<Promise<commander.Command>>(program.parseAsync(['node', 'script.js'], { from: 'electron' }));
expectType<Promise<commander.Command>>(program.parseAsync(['--option'], { from: 'user' }));
expectType<Promise<commander.Command>>(program.parseAsync(['node', 'script.js'] as const));

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

// aliases
expectType<commander.Command>(program.aliases(['first-alias', 'second-alias']));
expectType<commander.Command>(program.aliases(['first-alias', 'second-alias'] as const));
expectType<string[]>(program.aliases());

// usage
Expand Down

0 comments on commit f17ecbd

Please sign in to comment.