Repository: yargs/yargs-parser
Pull request #123 (2018-05-12 22:31:06)

feat: add `set-placeholder-key` configuration
If `true`, add a placeholder for each known key for which the corresponding CLI argument is not set.
The default value for the placeholder is `undefined`.

Follow up to https://github.com/yargs/yargs/pull/1105

pvdlg: It seems Appveyor build fails because [it installs the latest version of npm](https://github.com/yargs/yargs-parser/blob/master/appveyor.yml#L9) on Node 5 which is not supported.

Patch:
diff --git a/README.md b/README.md
index 6d6d0d4..9c43338 100644
--- a/README.md
+++ b/README.md
@@ -298,6 +298,27 @@ node example.js a -b -- x y
 { _: [ 'a' ], '--': [ 'x', 'y' ], b: true }
 ```
 
+### set placeholder key
+
+* default: `false`.
+* key: `set-placeholder-key`.
+
+Should a placeholder be added for keys not set via the corresponding CLI argument?
+
+_If disabled:_
+
+```sh
+node example.js -a 1 -c 2
+{ _: [], a: 1, c: 2 }
+```
+
+_If enabled:_
+
+```sh
+node example.js -a 1 -c 2
+{ _: [], a: 1, b: undefined, c: 2 }
+```
+
 ## Special Thanks
 
 The yargs project evolves from optimist and minimist. It owes its
diff --git a/index.js b/index.js
index 9889ac5..a3849bc 100644
--- a/index.js
+++ b/index.js
@@ -20,7 +20,8 @@ function parse (args, opts) {
     'duplicate-arguments-array': true,
     'flatten-duplicate-arrays': true,
     'populate--': false,
-    'combine-arrays': false
+    'combine-arrays': false,
+    'set-placeholder-key': false
   }, opts.configuration)
   var defaults = opts.default || {}
   var configObjects = opts.configObjects || []
@@ -44,41 +45,50 @@ function parse (args, opts) {
     configs: {},
     defaulted: {},
     nargs: {},
-    coercions: {}
+    coercions: {},
+    keys: []
   }
   var negative = /^-[0-9]+(\.[0-9]+)?/
   var negatedBoolean = new RegExp('^--' + configuration['negation-prefix'] + '(.+)')
 
   ;[].concat(opts.array).filter(Boolean).forEach(function (key) {
     flags.arrays[key] = true
+    flags.keys.push(key)
   })
 
   ;[].concat(opts.boolean).filter(Boolean).forEach(function (key) {
     flags.bools[key] = true
+    flags.keys.push(key)
   })
 
   ;[].concat(opts.string).filter(Boolean).forEach(function (key) {
     flags.strings[key] = true
+    flags.keys.push(key)
   })
 
   ;[].concat(opts.number).filter(Boolean).forEach(function (key) {
     flags.numbers[key] = true
+    flags.keys.push(key)
   })
 
   ;[].concat(opts.count).filter(Boolean).forEach(function (key) {
     flags.counts[key] = true
+    flags.keys.push(key)
   })
 
   ;[].concat(opts.normalize).filter(Boolean).forEach(function (key) {
     flags.normalize[key] = true
+    flags.keys.push(key)
   })
 
   Object.keys(opts.narg || {}).forEach(function (k) {
     flags.nargs[k] = opts.narg[k]
+    flags.keys.push(k)
   })
 
   Object.keys(opts.coerce || {}).forEach(function (k) {
     flags.coercions[k] = opts.coerce[k]
+    flags.keys.push(k)
   })
 
   if (Array.isArray(opts.config) || typeof opts.config === 'string') {
@@ -289,6 +299,7 @@ function parse (args, opts) {
   setConfigObjects()
   applyDefaultsAndAliases(argv, flags.aliases, defaults)
   applyCoercions(argv)
+  if (configuration['set-placeholder-key']) setPlaceholderKeys(argv)
 
   // for any counts either not in args or without an explicit default, set to 0
   Object.keys(flags.counts).forEach(function (key) {
@@ -556,6 +567,15 @@ function parse (args, opts) {
     })
   }
 
+  function setPlaceholderKeys (argv) {
+    flags.keys.forEach((key) => {
+      // don't set placeholder keys for dot notation options 'foo.bar'.
+      if (~key.indexOf('.')) return
+      if (typeof argv[key] === 'undefined') argv[key] = undefined
+    })
+    return argv
+  }
+
   function applyDefaultsAndAliases (obj, aliases, defaults) {
     Object.keys(defaults).forEach(function (key) {
       if (!hasKey(obj, key.split('.'))) {


Test patch:
diff --git a/test/yargs-parser.js b/test/yargs-parser.js
index 2a4ebc7..2910424 100644
--- a/test/yargs-parser.js
+++ b/test/yargs-parser.js
@@ -2324,6 +2324,65 @@ describe('yargs-parser', function () {
         result.should.have.property('--').and.deep.equal(['--not-a-flag', '-', '-h', '-multi', '--', 'eek'])
       })
     })
+
+    describe('set-placeholder-key', function () {
+      it('should not set placeholder key by default', function () {
+        var parsed = parser([], {
+          string: ['a']
+        })
+        parsed.should.not.have.property('a')
+      })
+
+      it('should set placeholder key to "undefined"', function () {
+        var parsed = parser([], {
+          array: ['a'],
+          boolean: ['b'],
+          string: ['c'],
+          number: ['d'],
+          count: ['e'],
+          normalize: ['f'],
+          narg: {g: 2},
+          coerce: {
+            h: function (arg) {
+              return arg
+            }
+          },
+          configuration: {'set-placeholder-key': true}
+        })
+        parsed.should.have.property('a')
+        expect(parsed.a).to.be.equal(undefined)
+        parsed.should.have.property('b')
+        expect(parsed.b).to.be.equal(undefined)
+        parsed.should.have.property('c')
+        expect(parsed.c).to.be.equal(undefined)
+        parsed.should.have.property('d')
+        expect(parsed.d).to.be.equal(undefined)
+        parsed.should.have.property('e')
+        expect(parsed.f).to.be.equal(undefined)
+        parsed.should.have.property('g')
+        expect(parsed.g).to.be.equal(undefined)
+        parsed.should.have.property('h')
+        expect(parsed.h).to.be.equal(undefined)
+      })
+
+      it('should not set placeholder for key with a default value', function () {
+        var parsed = parser([], {
+          string: ['a'],
+          default: {a: 'hello'},
+          configuration: {'set-placeholder-key': true}
+        })
+        parsed.a.should.equal('hello')
+      })
+
+      it('should not set placeholder key with dot notation', function () {
+        var parsed = parser([], {
+          string: ['a.b']
+        })
+        parsed.should.not.have.property('a')
+        parsed.should.not.have.property('b')
+        parsed.should.not.have.property('a.b')
+      })
+    })
   })
 
   // addresses: https://github.com/yargs/yargs-parser/issues/41