Skip to content

Commit fba00eb

Browse files
authored
feat: reworking how numbers are parsed (#104)
BREAKING CHANGE: strings that fail `Number.isSafeInteger()` are no longer coerced into numbers.
1 parent d137227 commit fba00eb

File tree

3 files changed

+50
-15
lines changed

3 files changed

+50
-15
lines changed

index.js

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -270,9 +270,7 @@ function parse (args, opts) {
270270
}
271271
}
272272
} else {
273-
argv._.push(
274-
flags.strings['_'] || !isNumber(arg) ? arg : Number(arg)
275-
)
273+
argv._.push(maybeCoerceNumber('_', arg))
276274
}
277275
}
278276

@@ -348,10 +346,8 @@ function parse (args, opts) {
348346
function setArg (key, val) {
349347
unsetDefaulted(key)
350348

351-
if (/-/.test(key) && !(flags.aliases[key] && flags.aliases[key].length) && configuration['camel-case-expansion']) {
352-
var c = camelCase(key)
353-
flags.aliases[key] = [c]
354-
newAliases[c] = true
349+
if (/-/.test(key) && configuration['camel-case-expansion']) {
350+
addNewAlias(key, camelCase(key))
355351
}
356352

357353
var value = processValue(key, val)
@@ -396,17 +392,23 @@ function parse (args, opts) {
396392
}
397393
}
398394

395+
function addNewAlias (key, alias) {
396+
if (!(flags.aliases[key] && flags.aliases[key].length)) {
397+
flags.aliases[key] = [alias]
398+
newAliases[alias] = true
399+
}
400+
if (!(flags.aliases[alias] && flags.aliases[alias].length)) {
401+
addNewAlias(alias, key)
402+
}
403+
}
404+
399405
function processValue (key, val) {
400406
// handle parsing boolean arguments --foo=true --bar false.
401407
if (checkAllAliases(key, flags.bools) || checkAllAliases(key, flags.counts)) {
402408
if (typeof val === 'string') val = val === 'true'
403409
}
404410

405-
var value = val
406-
if (!checkAllAliases(key, flags.strings) && !checkAllAliases(key, flags.coercions)) {
407-
if (isNumber(val)) value = Number(val)
408-
if (!isUndefined(val) && !isNumber(val) && checkAllAliases(key, flags.numbers)) value = NaN
409-
}
411+
var value = maybeCoerceNumber(key, val)
410412

411413
// increment a count given as arg (either no value or value parsed as boolean)
412414
if (checkAllAliases(key, flags.counts) && (isUndefined(value) || typeof value === 'boolean')) {
@@ -421,6 +423,14 @@ function parse (args, opts) {
421423
return value
422424
}
423425

426+
function maybeCoerceNumber (key, value) {
427+
if (!checkAllAliases(key, flags.strings) && !checkAllAliases(key, flags.coercions)) {
428+
const shouldCoerceNumber = isNumber(value) && configuration['parse-numbers'] && (Number.isSafeInteger(parseInt(value)))
429+
if (shouldCoerceNumber || (!isUndefined(value) && checkAllAliases(key, flags.numbers))) value = Number(value)
430+
}
431+
return value
432+
}
433+
424434
// set args from config.json file, this should be
425435
// applied last so that defaults can be applied.
426436
function setConfig (argv) {
@@ -602,7 +612,9 @@ function parse (args, opts) {
602612
flags.aliases[key].concat(key).forEach(function (x) {
603613
if (/-/.test(x) && configuration['camel-case-expansion']) {
604614
var c = camelCase(x)
605-
flags.aliases[key].push(c)
615+
if (flags.aliases[key].indexOf(c) === -1) {
616+
flags.aliases[key].push(c)
617+
}
606618
newAliases[c] = true
607619
}
608620
})
@@ -664,7 +676,6 @@ function parse (args, opts) {
664676
}
665677

666678
function isNumber (x) {
667-
if (!configuration['parse-numbers']) return false
668679
if (typeof x === 'number') return true
669680
if (/^0x[0-9a-f]+$/i.test(x)) return true
670681
return /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(x)

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
"chai": "^3.5.0",
3030
"coveralls": "^2.11.12",
3131
"mocha": "^3.0.1",
32-
"nyc": "^10.0.0",
32+
"nyc": "^11.2.1",
3333
"standard": "^10.0.2",
3434
"standard-version": "^4.0.0"
3535
},

test/yargs-parser.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1955,6 +1955,17 @@ describe('yargs-parser', function () {
19551955
})
19561956
expect(parsed._[0]).to.equal('5')
19571957
})
1958+
1959+
it('parses number if option explicitly set to number type', function () {
1960+
var parsed = parser(['--foo', '5', '--bar', '6'], {
1961+
number: 'bar',
1962+
configuration: {
1963+
'parse-numbers': false
1964+
}
1965+
})
1966+
expect(parsed['foo']).to.equal('5')
1967+
expect(parsed['bar']).to.equal(6)
1968+
})
19581969
})
19591970

19601971
describe('boolean negation', function () {
@@ -2440,4 +2451,17 @@ describe('yargs-parser', function () {
24402451
})
24412452
argv.a.should.deep.equal(['a.txt', 'b.txt'])
24422453
})
2454+
2455+
// see: https://github.com/yargs/yargs/issues/963
2456+
it('does not magically convert numeric strings larger than Number.MAX_SAFE_INTEGER', () => {
2457+
const argv = parser([ '--foo', '93940495950949399948393' ])
2458+
argv.foo.should.equal('93940495950949399948393')
2459+
})
2460+
2461+
it('converts numeric options larger than Number.MAX_SAFE_INTEGER to number', () => {
2462+
const argv = parser([ '--foo', '93940495950949399948393' ], {
2463+
number: ['foo']
2464+
})
2465+
argv.foo.should.equal(9.39404959509494e+22)
2466+
})
24432467
})

0 commit comments

Comments
 (0)