diff --git a/src/utils.ts b/src/utils.ts index 1ce99e03..56bdacb9 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -46,10 +46,11 @@ interface MriOptions { [k: string]: string[] } boolean: string[] + string: string[] } export function getMriOptions(options: Option[]): MriOptions { - const result: MriOptions = { alias: {}, boolean: [] } + const result: MriOptions = { alias: {}, boolean: [], string: [] } for (const [index, option] of options.entries()) { // We do not set default values in mri options @@ -78,6 +79,22 @@ export function getMriOptions(options: Option[]): MriOptions { result.boolean.push(option.names[0]) } } + // Keep the raw string for `type: [String]` so that mri doesn't coerce + // numeric-looking values (e.g. `0123` or `+1`) to numbers first + if ( + typeof option.required === 'boolean' && + Array.isArray(option.config.type) && + option.config.type[0] === String + ) { + for (const name of removeBrackets(option.rawName).split(',')) { + result.string.push( + name + .trim() + .replace(/^-{1,2}/, '') + .replaceAll('.*', ''), + ) + } + } } return result diff --git a/tests/index.test.ts b/tests/index.test.ts index bbb176b4..35729533 100644 --- a/tests/index.test.ts +++ b/tests/index.test.ts @@ -151,6 +151,53 @@ test('array types with transformFunction', () => { expect(options.scale).toEqual(true) }) +test('array types with String transformFunction keep raw values', () => { + const cli = cac() + + cli + .option('-p, --phone-number ', 'Phone number', { + type: [String], + }) + .option('--code [code]', 'Code', { type: [String] }) + .option('--port ', 'Port') + .option('--ids ', 'IDs', { type: [] }) + + const { options } = cli.parse([ + 'node', + 'bin', + '--phone-number', + '+310000', + '--code', + '01234', + '--code', + '1e3', + '--port', + '0080', + '--ids', + '007', + ]) + expect(options.phoneNumber).toEqual(['+310000']) + expect(options.code).toEqual(['01234', '1e3']) + // Options without a String transform are coerced as before + expect(options.port).toBe(80) + expect(options.ids).toEqual([7]) + + const { options: options2 } = cli.parse([ + 'node', + 'bin', + '-p', + '0x10', + '--code', + '日本語', + ]) + expect(options2.p).toBe('0x10') + expect(options2.phoneNumber).toEqual(['0x10']) + expect(options2.code).toEqual(['日本語']) + + const { options: options3 } = cli.parse(['node', 'bin', '--code', '']) + expect(options3.code).toEqual(['']) +}) + test('throw on unknown options', () => { const cli = cac()