diff --git a/README.md b/README.md index 95ef4b22..12b8fb47 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,8 @@ Parses command line arguments returning a simple mapping of keys and values. * `opts.array`: indicate that keys should be parsed as an array: `{array: ['foo', 'bar']}`. * `opts.boolean`: arguments should be parsed as booleans: `{boolean: ['x', 'y']}`. * `opts.config`: indicate a key that represents a path to a configuration file (this file will be loaded and parsed). + * `opts.coerce`: provide a custom function to coerce a value from the argument provided, + e.g., `{coerce: {foo: function (arg, cb) {return cb(err, modifiedArg)}}}`. * `opts.count`: indicate a key that should be used as a counter, e.g., `-vvv` = `{v: 3}`. * `opts.default`: provide default values for keys: `{default: {x: 33, y: 'hello world!'}}`. * `opts.envPrefix`: environment variables (`process.env`) with the prefix provided should be parsed. diff --git a/index.js b/index.js index 26cbfd1d..6dce5569 100644 --- a/index.js +++ b/index.js @@ -37,7 +37,8 @@ function parse (args, opts) { normalize: {}, configs: {}, defaulted: {}, - nargs: {} + nargs: {}, + coercions: {} } ;[].concat(opts.array).filter(Boolean).forEach(function (key) { @@ -68,6 +69,10 @@ function parse (args, opts) { flags.nargs[k] = opts.narg[k] }) + Object.keys(opts.coerce || {}).forEach(function (k) { + flags.coercions[k] = opts.coerce[k] + }) + if (Array.isArray(opts.config) || typeof opts.config === 'string') { ;[].concat(opts.config).filter(Boolean).forEach(function (key) { flags.configs[key] = true @@ -331,7 +336,7 @@ function parse (args, opts) { } var value = val - if (!checkAllAliases(key, flags.strings)) { + if (!checkAllAliases(key, flags.strings) && !checkAllAliases(key, flags.coercions)) { if (isNumber(val)) value = Number(val) if (!isUndefined(val) && !isNumber(val) && checkAllAliases(key, flags.numbers)) value = NaN } @@ -512,6 +517,13 @@ function parse (args, opts) { }) var key = keys[keys.length - 1] + var coerce = checkAllAliases(key, flags.coercions) + if (coerce) { + coerce(value, function (err, val) { + error = err + value = val + }) + } if (value === increment) { o[key] = increment(o[key]) diff --git a/test/yargs-parser.js b/test/yargs-parser.js index f35d8ff4..2bcf394e 100644 --- a/test/yargs-parser.js +++ b/test/yargs-parser.js @@ -1933,4 +1933,77 @@ describe('yargs-parser', function () { parsed.f.should.deep.equal([]) parsed.files.should.deep.equal([]) }) + + describe('coerce', function () { + it('applies coercion function to simple arguments', function () { + var parsed = parser(['--foo', '99'], { + coerce: { + foo: function (arg, cb) { + return cb(null, arg * -1) + } + } + }) + parsed.foo.should.equal(-99) + }) + + it('applies coercion function to aliases', function () { + var parsed = parser(['--foo', '99'], { + coerce: { + f: function (arg, cb) { + return cb(null, arg * -1) + } + }, + alias: { + f: ['foo'] + } + }) + parsed.foo.should.equal(-99) + parsed.f.should.equal(-99) + }) + + it('applies coercion function to an array', function () { + var parsed = parser(['--foo', '99', '-f', '33'], { + coerce: { + f: function (arg, cb) { + return cb(null, arg * -1) + } + }, + array: ['foo'], + alias: { + f: ['foo'] + } + }) + parsed.f.should.deep.equal([-99, -33]) + parsed.foo.should.deep.equal([-99, -33]) + }) + + // see: https://github.com/yargs/yargs/issues/550 + it('coercion function can be used to parse large #s', function () { + var fancyNumberParser = function (arg, cb) { + if (arg.length > 10) return cb(null, arg) + else return cb(null, parseInt(arg)) + } + var parsed = parser(['--foo', '88888889999990000998989898989898', '--bar', '998'], { + coerce: { + foo: fancyNumberParser, + bar: fancyNumberParser + } + }) + ;(typeof parsed.foo).should.equal('string') + parsed.foo.should.equal('88888889999990000998989898989898') + ;(typeof parsed.bar).should.equal('number') + parsed.bar.should.equal(998) + }) + + it('populates argv.error, if an error is returned', function () { + var parsed = parser.detailed(['--foo', '99'], { + coerce: { + foo: function (arg, cb) { + return cb(Error('banana'), arg * -1) + } + } + }) + parsed.error.message.should.equal('banana') + }) + }) })