diff --git a/lib/yargs-factory.ts b/lib/yargs-factory.ts index 2bfc5224a..a7c77f564 100644 --- a/lib/yargs-factory.ts +++ b/lib/yargs-factory.ts @@ -380,20 +380,26 @@ export class YargsInstance { if (!value) { throw new YError('coerce callback must be provided'); } + + // Handled multiple above, down to one key. + const coerceKey = keys; // This noop tells yargs-parser about the existence of the option - // represented by "keys", so that it can apply camel case expansion + // represented by "coerceKey", so that it can apply camel case expansion // if needed: - this.#options.key[keys] = true; + this.#options.key[coerceKey] = true; this.#globalMiddleware.addCoerceMiddleware( ( argv: Arguments, yargs: YargsInstance ): Partial | Promise> => { - let aliases: Dictionary; + // Narrow down the possible keys to the ones present in argv. + const coerceKeyAliases = yargs.getAliases()[coerceKey] ?? []; + const argvKeys = [coerceKey, ...coerceKeyAliases].filter(key => + Object.prototype.hasOwnProperty.call(argv, key) + ); - // Skip coerce logic if related arg was not provided - const shouldCoerce = Object.prototype.hasOwnProperty.call(argv, keys); - if (!shouldCoerce) { + // Skip coerce if nothing to coerce. + if (argvKeys.length === 0) { return argv; } @@ -401,19 +407,12 @@ export class YargsInstance { Partial | Promise> | any >( () => { - aliases = yargs.getAliases(); - return value(argv[keys]); + return value(argv[argvKeys[0]]); }, (result: any): Partial => { - argv[keys] = result; - const stripAliased = yargs - .getInternalMethods() - .getParserConfiguration()['strip-aliased']; - if (aliases[keys] && stripAliased !== true) { - for (const alias of aliases[keys]) { - argv[alias] = result; - } - } + argvKeys.forEach(key => { + argv[key] = result; + }); return argv; }, (err: Error): Partial | Promise> => { @@ -421,7 +420,7 @@ export class YargsInstance { } ); }, - keys + coerceKey ); return this; } diff --git a/test/yargs.cjs b/test/yargs.cjs index 42b4b83b8..2011a8288 100644 --- a/test/yargs.cjs +++ b/test/yargs.cjs @@ -2238,6 +2238,76 @@ describe('yargs dsl tests', () => { .getHelp(); help.should.match(/option2 description/); }); + + it('argv includes coerced aliases', () => { + const argv = yargs('--foo bar') + .option('foo', { + coerce: s => s.toUpperCase(), + alias: 'f', + }) + .parse(); + argv['foo'].should.equal('BAR'); + argv['f'].should.equal('BAR'); + }); + + it('argv includes coerced camelCase', () => { + const argv = yargs('--foo-foo bar') + .option('foo-foo', { + coerce: s => s.toUpperCase(), + }) + .parse(); + argv['foo-foo'].should.equal('BAR'); + argv['fooFoo'].should.equal('BAR'); + }); + + it('coerce still works when key used for coerce is not explicitly present in argv', () => { + const argv = yargs('--foo-foo bar') + .option('foo-foo') + .coerce('foo-foo', s => s.toUpperCase()) + .parserConfiguration({'strip-dashed': true}) + .parse(); + expect(argv['foo-foo']).to.equal(undefined); + argv['fooFoo'].should.equal('BAR'); + }); + + it('argv does not include stripped aliases', () => { + const argv = yargs('-f bar') + .option('foo-foo', { + coerce: s => s.toUpperCase(), + alias: 'f', + }) + .parserConfiguration({'strip-aliased': true}) + .parse(); + argv['foo-foo'].should.equal('BAR'); + argv['fooFoo'].should.equal('BAR'); + expect(argv['f']).to.equal(undefined); + }); + + it('argv does not include stripped dashes', () => { + const argv = yargs('-f bar') + .option('foo-foo', { + coerce: s => s.toUpperCase(), + alias: 'f', + }) + .parserConfiguration({'strip-dashed': true}) + .parse(); + expect(argv['foo-foo']).to.equal(undefined); + argv['fooFoo'].should.equal('BAR'); + argv['f'].should.equal('BAR'); + }); + + it('argv does not include disabled camel-case-expansion', () => { + const argv = yargs('-f bar') + .option('foo-foo', { + coerce: s => s.toUpperCase(), + alias: 'f', + }) + .parserConfiguration({'camel-case-expansion': false}) + .parse(); + argv['foo-foo'].should.equal('BAR'); + expect(argv['fooFoo']).to.equal(undefined); + argv['f'].should.equal('BAR'); + }); }); describe('stop parsing', () => {