Repository: yargs/yargs-parser
Pull request #42 (2016-08-08 01:46:26)

feat: added coerce option, for providing specialized argument parsing


Patch:
diff --git a/README.md b/README.md
index 95ef4b2..12b8fb4 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 26cbfd1..6dce556 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])


Test patch:
diff --git a/test/yargs-parser.js b/test/yargs-parser.js
index f35d8ff..2bcf394 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')
+    })
+  })
 })