diff --git a/lib/yargs-parser.ts b/lib/yargs-parser.ts index 48a2d0f2..cbb202e4 100644 --- a/lib/yargs-parser.ts +++ b/lib/yargs-parser.ts @@ -429,8 +429,18 @@ export class YargsParser { }) if (configuration['camel-case-expansion'] && configuration['strip-dashed']) { - Object.keys(argv).filter(key => key !== '--' && key.includes('-')).forEach(key => { - delete argv[key] + const keys = new Set([...Object.keys(argv), ...Object.keys(flags.aliases)]) + keys.forEach(key => { + if (key === '--' || !key.includes('-')) return + const parts = configuration['dot-notation'] ? key.split('.') : [key] + if (parts.length > 1 && typeof checkAllAliases(parts[0], flags.coercions) === 'function') return + let target = argv + for (const part of parts.slice(0, -1)) { + const property = sanitizeKey(part) + if (!target || typeof target !== 'object' || !Object.prototype.hasOwnProperty.call(target, property)) return + target = target[property] + } + if (target && typeof target === 'object') delete target[sanitizeKey(parts[parts.length - 1])] }) } diff --git a/test/yargs-parser.mjs b/test/yargs-parser.mjs index 4c692375..6140028c 100644 --- a/test/yargs-parser.mjs +++ b/test/yargs-parser.mjs @@ -14,6 +14,52 @@ const require = createRequire(import.meta.url); should() describe('yargs-parser', function () { + it('preserves parsed-looking keys in plain and frozen coerce results', function () { + for (const freeze of [value => value, Object.freeze]) { + const value = freeze({ 'literal-key': 'own', 'inner-dash': 'replacement' }) + const result = parser.detailed('--outer.inner-dash value', { + coerce: { outer: () => value }, + configuration: { 'strip-dashed': true } + }) + expect(result.error).to.equal(null) + expect(result.argv.outer).to.equal(value) + expect(result.argv.outer['inner-dash']).to.equal('replacement') + } + }) + + it('strips dashed option keys at nested paths', function () { + const result = parser('--outer.inner-dash true --outer.deep.third-dash 5', { + configuration: { 'strip-dashed': true } + }) + expect(result.outer).to.deep.equal({ innerDash: 'true', deep: { thirdDash: 5 } }) + }) + + it('strips nested dashed configuration keys while retaining camel aliases', function () { + const result = parser([], { + configObjects: [{ outer: { 'inner-dash': 'value' } }], + configuration: { 'strip-dashed': true } + }) + expect(result.outer).to.deep.equal({ innerDash: 'value' }) + }) + + it('keeps literal dotted camel aliases when dot notation is disabled', function () { + const result = parser('--outer.inner-dash value', { + configuration: { 'strip-dashed': true, 'dot-notation': false } + }) + expect(result['outer.innerDash']).to.equal('value') + expect(result).not.to.have.property('outer.inner-dash') + }) + + it('does not strip arbitrary keys from a coerced option object', function () { + const value = { 'literal-key': 'value' } + const result = parser('--option value', { + coerce: { option: () => value }, + configuration: { 'strip-dashed': true } + }) + expect(result.option).to.equal(value) + expect(result.option['literal-key']).to.equal('value') + }) + it('should parse a "short boolean"', function () { const parse = parser(['-b']) parse.should.not.have.property('--')