From 5d7ad989a851398587a0349cdd15344769b4cd79 Mon Sep 17 00:00:00 2001 From: Peter Hedenskog Date: Fri, 6 Sep 2019 21:41:09 +0200 Subject: [PATCH] feat: make it possible to merge configurations when extending other config. (#1411) --- docs/advanced.md | 3 +- lib/apply-extends.js | 18 ++++++++-- test/fixtures/extends/config_deep.json | 12 +++++++ test/yargs.js | 50 ++++++++++++++++++++++++++ yargs.js | 4 +-- 5 files changed, 82 insertions(+), 5 deletions(-) create mode 100644 test/fixtures/extends/config_deep.json diff --git a/docs/advanced.md b/docs/advanced.md index 9605cc576..8a0bb2a1a 100644 --- a/docs/advanced.md +++ b/docs/advanced.md @@ -439,7 +439,8 @@ yargs.parserConfiguration({ "camel-case-expansion": true, "dot-notation": true, "parse-numbers": true, - "boolean-negation": true + "boolean-negation": true, + "deep-merge-config": false }) ``` diff --git a/lib/apply-extends.js b/lib/apply-extends.js index 864567a61..20ded5e07 100644 --- a/lib/apply-extends.js +++ b/lib/apply-extends.js @@ -16,7 +16,21 @@ function getPathToDefaultConfig (cwd, pathToExtend) { return path.resolve(cwd, pathToExtend) } -function applyExtends (config, cwd) { +function mergeDeep (config1, config2) { + const target = {} + const isObject = obj => obj && typeof obj === 'object' && !Array.isArray(obj) + Object.assign(target, config1) + for (let key of Object.keys(config2)) { + if (isObject(config2[key]) && isObject(target[key])) { + target[key] = mergeDeep(config1[key], config2[key]) + } else { + target[key] = config2[key] + } + } + return target +} + +function applyExtends (config, cwd, mergeExtends) { let defaultConfig = {} if (Object.prototype.hasOwnProperty.call(config, 'extends')) { @@ -47,7 +61,7 @@ function applyExtends (config, cwd) { previouslyVisitedConfigs = [] - return Object.assign({}, defaultConfig, config) + return mergeExtends ? mergeDeep(defaultConfig, config) : Object.assign({}, defaultConfig, config) } module.exports = applyExtends diff --git a/test/fixtures/extends/config_deep.json b/test/fixtures/extends/config_deep.json new file mode 100644 index 000000000..d4e1bdbbf --- /dev/null +++ b/test/fixtures/extends/config_deep.json @@ -0,0 +1,12 @@ +{ + "a": { + "b": 15, + "c": 12, + "d": [1,2,3], + "e": [1], + "f": "yes" + }, + "test": { + "yes": 1 + } +} diff --git a/test/yargs.js b/test/yargs.js index 29ba21454..8be9e7a28 100644 --- a/test/yargs.js +++ b/test/yargs.js @@ -1374,6 +1374,56 @@ describe('yargs dsl tests', () => { argv.c.should.equal(201) argv.z.should.equal(15) }) + + it('deep merge objects when extending when configured', () => { + const argv = yargs() + .parserConfiguration({ 'deep-merge-config': true }) + .config({ + extends: './test/fixtures/extends/config_deep.json', + a: { + b: 11, + d: [5, 2], + f: 'no', + g: { + h: 122 + }, + i: [1, 2] + } + }) + .parse() + + argv.a.b.should.equal(11) + argv.a.c.should.equal(12) + argv.a.d.should.deep.equal([5, 2]) + argv.a.e.should.equal(1) + argv.a.f.should.equal('no') + argv.a.g.h.should.equal(122) + argv.a.i.should.deep.equal([1, 2]) + argv.test.yes.should.equal(1) + }) + + it('do not merge objects by default when extending', () => { + const argv = yargs() + .config({ + extends: './test/fixtures/extends/config_deep.json', + a: { + b: 11, + d: [5, 2], + f: 'no', + g: { + h: 122 + } + } + }) + .parse() + + argv.a.b.should.equal(11) + argv.a.should.not.have.property('c') + argv.a.d.should.deep.equal([5, 2]) + argv.a.should.not.have.property('e') + argv.a.f.should.equal('no') + argv.a.g.h.should.equal(122) + }) }) }) diff --git a/yargs.js b/yargs.js index 794cf0255..a4ed28e5a 100644 --- a/yargs.js +++ b/yargs.js @@ -330,7 +330,7 @@ function Yargs (processArgs, cwd, parentRequire) { argsert('[object|string] [string|function] [function]', [key, msg, parseFn], arguments.length) // allow a config object to be provided directly. if (typeof key === 'object') { - key = applyExtends(key, cwd) + key = applyExtends(key, cwd, self.getParserConfiguration()['deep-merge-config']) options.configObjects = (options.configObjects || []).concat(key) return self } @@ -504,7 +504,7 @@ function Yargs (processArgs, cwd, parentRequire) { // If an object exists in the key, add it to options.configObjects if (obj[key] && typeof obj[key] === 'object') { - conf = applyExtends(obj[key], rootPath || cwd) + conf = applyExtends(obj[key], rootPath || cwd, self.getParserConfiguration()['deep-merge-config']) options.configObjects = (options.configObjects || []).concat(conf) }