Repository: yargs/yargs-parser
Pull request #15 (2016-03-29 10:29:00)

Allow nested options in config files
It solves yargs/yargs#292.

`setConfig()` now checks the config object recursively, and calls `setArg()` for every nested key if that key isn't in argv or has the default value.



Patch:
diff --git a/index.js b/index.js
index 0442922..6b9106c 100644
--- a/index.js
+++ b/index.js
@@ -406,14 +406,7 @@ function parse (args, opts) {
             config = require(resolvedConfigPath)
           }
 
-          Object.keys(config).forEach(function (key) {
-            // setting arguments via CLI takes precedence over
-            // values within the config file.
-            if (argv[key] === undefined || (flags.defaulted[key])) {
-              delete argv[key]
-              setArg(key, config[key])
-            }
-          })
+          setConfigObject(config)
         } catch (ex) {
           if (argv[configKey]) error = Error(__('Invalid JSON config file: %s', configPath))
         }
@@ -421,6 +414,26 @@ function parse (args, opts) {
     })
   }
 
+  // set args from config object.
+  // it recursively checks nested objects.
+  function setConfigObject (config, prev) {
+    Object.keys(config).forEach(function (key) {
+      var value = config[key]
+      var fullKey = prev ? prev + '.' + key : key
+
+      if (Object.prototype.toString.call(value) === '[object Object]') {
+        // if the value is an object but not an array, check nested object
+        setConfigObject(value, fullKey)
+      } else {
+        // setting arguments via CLI takes precedence over
+        // values within the config file.
+        if (!hasKey(argv, fullKey.split('.')) || (flags.defaulted[fullKey])) {
+          setArg(fullKey, value)
+        }
+      }
+    })
+  }
+
   function applyEnvVars (argv, configOnly) {
     if (typeof envPrefix === 'undefined') return
 


Test patch:
diff --git a/test/fixtures/nested_config.json b/test/fixtures/nested_config.json
new file mode 100644
index 0000000..973083b
--- /dev/null
+++ b/test/fixtures/nested_config.json
@@ -0,0 +1,8 @@
+{
+  "a": "a",
+  "nested": {
+    "foo": "baz",
+    "bar": "bar"
+  },
+  "b": "b"
+}
\ No newline at end of file
diff --git a/test/yargs-parser.js b/test/yargs-parser.js
index fdc75fa..75eaf10 100644
--- a/test/yargs-parser.js
+++ b/test/yargs-parser.js
@@ -473,6 +473,37 @@ describe('yargs-parser', function () {
       expect(argv.error).to.equal(null)
     })
 
+    it('should load nested options from config file', function () {
+      var jsonPath = path.resolve(__dirname, './fixtures/nested_config.json')
+      var argv = parser(['--settings', jsonPath, '--nested.foo', 'bar'], {
+        config: ['settings']
+      })
+
+      argv.should.have.property('a', 'a')
+      argv.should.have.property('b', 'b')
+      argv.should.have.property('nested').and.deep.equal({
+        foo: 'bar',
+        bar: 'bar'
+      })
+    })
+
+    it('should use nested value from config file, if argv value is using default value', function () {
+      var jsonPath = path.resolve(__dirname, './fixtures/nested_config.json')
+      var argv = parser(['--settings', jsonPath], {
+        config: ['settings'],
+        default: {
+          'nested.foo': 'banana'
+        }
+      })
+
+      argv.should.have.property('a', 'a')
+      argv.should.have.property('b', 'b')
+      argv.should.have.property('nested').and.deep.equal({
+        foo: 'baz',
+        bar: 'bar'
+      })
+    })
+
     it('allows a custom parsing function to be provided', function () {
       var jsPath = path.resolve(__dirname, './fixtures/config.txt')
       var argv = parser([ '--settings', jsPath, '--foo', 'bar' ], {