diff --git a/index.js b/index.js index ab8ab9f..ca49e94 100644 --- a/index.js +++ b/index.js @@ -25,6 +25,10 @@ function mixin(target, val, key) { let obj = target[key]; if (isObject(val) && isObject(obj)) { mixinDeep(obj, val); + } else if (!(key in target) && isObject(val)) { + // `target` can be mutated by later mixins. Therefore, if we are currently adding an object to `target`, + // it could be mutated later. We need to make sure that mutation will not change the original data. + target[key] = {...val}; } else { target[key] = val; } diff --git a/package.json b/package.json index 48d470c..0386923 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "mixin-deep", "description": "Deeply mix the properties of objects into the first object. Like merge-deep, but doesn't clone. No dependencies.", - "version": "2.0.1", + "version": "3.0.0", "homepage": "https://github.com/jonschlinkert/mixin-deep", "author": "Jon Schlinkert (https://github.com/jonschlinkert)", "repository": "jonschlinkert/mixin-deep", diff --git a/test.js b/test.js index 396a580..92f18c0 100644 --- a/test.js +++ b/test.js @@ -36,6 +36,19 @@ describe('.mixinDeep()', function() { assert.notDeepEqual(actual, obj3); }); + it('should copy properties onto the first object without modifying other sub-objects', function() { + const obj1 = { x: { a: 0, b: 1 } }; + const obj2 = { x: { c: 2, d: 3 } }; + const obj3 = { x: { a: 4, d: 5 } }; + + const actual = { x: { a: 4, b: 1, c: 2, d: 5 } }; + + assert.deepEqual(mixinDeep({}, obj1, obj2, obj3), actual); + assert.notDeepEqual(actual, obj1); + assert.notDeepEqual(actual, obj2); + assert.notDeepEqual(actual, obj3); + }); + it('should mixin nested object properties', function() { const obj1 = { a: { b: 1, c: 1, d: { e: 1, f: 1 } } }; const obj2 = { a: { b: 2, d: { f: 'f' } } }; @@ -79,7 +92,7 @@ describe('.mixinDeep()', function() { const actual = mixinDeep({}, obj1, obj2); assert.deepEqual(actual, { a: { b: 1, c: 2 } }); - assert.deepEqual(actual.a, obj1.a); + assert.notDeepEqual(actual.a, obj1.a); assert.notDeepEqual(actual.a, obj2.a); });