From 4dac5b8c2f03488c31d40f075075d2ac43134412 Mon Sep 17 00:00:00 2001 From: Landon Yarrington <33426811+jly36963@users.noreply.github.com> Date: Sat, 14 May 2022 16:07:06 -0600 Subject: [PATCH] fix: passed arguments should take precedence over values in config (#2100) --- lib/command.ts | 11 ++++++----- lib/yargs-factory.ts | 24 ++++++++++++++++++++++++ test/command.cjs | 25 +++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 5 deletions(-) diff --git a/lib/command.ts b/lib/command.ts index ff9da5b1b..2d4f462d7 100644 --- a/lib/command.ts +++ b/lib/command.ts @@ -593,19 +593,20 @@ export class CommandInstance { positionalKeys.push(...parsed.aliases[key]); }); - const defaults = yargs.getOptions().default; Object.keys(parsed.argv).forEach(key => { if (positionalKeys.includes(key)) { // any new aliases need to be placed in positionalMap, which // is used for validation. if (!positionalMap[key]) positionalMap[key] = parsed.argv[key]; // Addresses: https://github.com/yargs/yargs/issues/1637 - // If both positionals/options provided, no default was set, + // If both positionals/options provided, + // and no default or config values were set for that key, // and if at least one is an array: don't overwrite, combine. if ( - !Object.hasOwnProperty.call(defaults, key) && - Object.hasOwnProperty.call(argv, key) && - Object.hasOwnProperty.call(parsed.argv, key) && + !yargs.isInConfigs(key) && + !yargs.isDefaulted(key) && + Object.prototype.hasOwnProperty.call(argv, key) && + Object.prototype.hasOwnProperty.call(parsed.argv, key) && (Array.isArray(argv[key]) || Array.isArray(parsed.argv[key])) ) { argv[key] = ([] as string[]).concat(argv[key], parsed.argv[key]); diff --git a/lib/yargs-factory.ts b/lib/yargs-factory.ts index 279d8bcce..b806688e1 100644 --- a/lib/yargs-factory.ts +++ b/lib/yargs-factory.ts @@ -869,6 +869,30 @@ export class YargsInstance { this.#validation.implies(key, value); return this; } + // Check defaults for key (and camel case version of key) + isDefaulted(key: string): boolean { + const {default: defaults} = this.getOptions(); + return ( + Object.prototype.hasOwnProperty.call(defaults, key) || + Object.prototype.hasOwnProperty.call( + defaults, + this.#shim.Parser.camelCase(key) + ) + ); + } + // Check each config for key (and camel case version of key) + isInConfigs(key: string): boolean { + const {configObjects} = this.getOptions(); + return ( + configObjects.some(c => Object.prototype.hasOwnProperty.call(c, key)) || + configObjects.some(c => + Object.prototype.hasOwnProperty.call( + c, + this.#shim.Parser.camelCase(key) + ) + ) + ); + } locale(locale?: string): YargsInstance | string { argsert('[string]', [locale], arguments.length); if (!locale) { diff --git a/test/command.cjs b/test/command.cjs index 867f8f9b5..d5f94b8cb 100644 --- a/test/command.cjs +++ b/test/command.cjs @@ -264,6 +264,31 @@ describe('Command', () => { .parse('cmd apples cherries grapes'); }); + it('does not combine config values and provided values', () => { + yargs('foo bar baz qux') + .command({ + command: '$0 [arg-2] [arg-3..]', + desc: 'default description', + builder: yargs => + yargs + .option('arg-1', {type: 'string'}) + .option('arg-2', {type: 'string'}) + .option('arg-3', {type: 'string'}) + .config({ + arg2: 'bar', + arg3: ['baz', 'qux'], + }), + handler: argv => { + argv.arg1.should.equal('foo'); + argv.arg2.should.equal('bar'); + argv.arg3.should.deep.equal(['baz', 'qux']); + argv['arg-3'].should.deep.equal(['baz', 'qux']); + }, + }) + .strict() + .parse(); + }); + it('does not overwrite options in argv if variadic and preserves falsy values', () => { yargs .command({