diff --git a/README.markdown b/README.markdown deleted file mode 100644 index d1d591e..0000000 --- a/README.markdown +++ /dev/null @@ -1,141 +0,0 @@ -deepmerge -========= - -> ~540B gzipped, ~1.1kB minified - -Merge the enumerable attributes of two objects deeply. - -the future ----------- - -Should we publish a version 2? [Give your opinion.](https://github.com/KyleAMathews/deepmerge/issues/72) - -example -======= - - - -```js -var x = { - foo: { bar: 3 }, - array: [{ - does: 'work', - too: [ 1, 2, 3 ] - }] -} - -var y = { - foo: { baz: 4 }, - quux: 5, - array: [{ - does: 'work', - too: [ 4, 5, 6 ] - }, { - really: 'yes' - }] -} - -var expected = { - foo: { - bar: 3, - baz: 4 - }, - array: [{ - does: 'work', - too: [ 1, 2, 3, 4, 5, 6 ] - }, { - really: 'yes' - }], - quux: 5 -} - -merge(x, y) // => expected -``` - -methods -======= - -``` -var merge = require('deepmerge') -``` - -merge(x, y, [options]) ------------ - -Merge two objects `x` and `y` deeply, returning a new merged object with the -elements from both `x` and `y`. - -If an element at the same key is present for both `x` and `y`, the value from -`y` will appear in the result. - -Merging creates a new object, so that neither `x` or `y` are be modified. However, child objects on `x` or `y` are copied over - if you want to copy all values, you must pass `true` to the clone option. - -merge.all(arrayOfObjects, [options]) ------------ - -Merges two or more objects into a single result object. - -```js -var x = { foo: { bar: 3 } } -var y = { foo: { baz: 4 } } -var z = { bar: 'yay!' } - -var expected = { foo: { bar: 3, baz: 4 }, bar: 'yay!' } - -merge.all([x, y, z]) // => expected -``` - -### options - -#### arrayMerge - -The merge will also merge arrays and array values by default. However, there are nigh-infinite valid ways to merge arrays, and you may want to supply your own. You can do this by passing an `arrayMerge` function as an option. - -```js -function concatMerge(destinationArray, sourceArray, options) { - destinationArray // => [1, 2, 3] - sourceArray // => [3, 2, 1] - options // => { arrayMerge: concatMerge } - return destinationArray.concat(sourceArray) -} -merge([1, 2, 3], [3, 2, 1], { arrayMerge: concatMerge }) // => [1, 2, 3, 3, 2, 1] -``` - -To prevent arrays from being merged: - -```js -const dontMerge = (destination, source) => source -const output = merge({ coolThing: [1,2,3] }, { coolThing: ['a', 'b', 'c'] }, { arrayMerge: dontMerge }) -output // => { coolThing: ['a', 'b', 'c'] } -``` - -#### clone - -Defaults to `false`. If `clone` is `true` then both `x` and `y` are recursively cloned as part of the merge. - -install -======= - -With [npm](http://npmjs.org) do: - -```sh -npm install deepmerge -``` - -Just want to download the file without using any package managers/bundlers? [Download the UMD version from unpkg.com](https://unpkg.com/deepmerge/dist/umd.js). - -test -==== - -With [npm](http://npmjs.org) do: - -```sh -npm test -``` - -license -======= - -MIT diff --git a/changelog.md b/changelog.md index 0f824d8..8a9bb06 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,11 @@ +# [2.0.0](https://github.com/KyleAMathews/deepmerge/releases/tag/v2.0.0) + +- breaking: the array merge algorithm has changed from a complicated thing to `target.concat(source).map(element => cloneUnlessOtherwiseSpecified(element, optionsArgument))` +- breaking: The `clone` option now defaults to `true` +- feature: `merge.all` now accepts an array of any size, even 0 or 1 elements + +See [pull request 77](https://github.com/KyleAMathews/deepmerge/pull/77). + # [1.5.2](https://github.com/KyleAMathews/deepmerge/releases/tag/v1.5.2) - fix: no longer attempts to merge React elements [#76](https://github.com/KyleAMathews/deepmerge/issues/76) diff --git a/index.js b/index.js index 9ec6c51..15bca12 100644 --- a/index.js +++ b/index.js @@ -1,70 +1,64 @@ var isMergeableObject = require('is-mergeable-object') function emptyTarget(val) { - return Array.isArray(val) ? [] : {} + return Array.isArray(val) ? [] : {} } -function cloneIfNecessary(value, optionsArgument) { - var clone = optionsArgument && optionsArgument.clone === true - return (clone && isMergeableObject(value)) ? deepmerge(emptyTarget(value), value, optionsArgument) : value +function cloneUnlessOtherwiseSpecified(value, optionsArgument) { + var clone = !optionsArgument || optionsArgument.clone !== false + + return (clone && isMergeableObject(value)) + ? deepmerge(emptyTarget(value), value, optionsArgument) + : value } function defaultArrayMerge(target, source, optionsArgument) { - var destination = target.slice() - source.forEach(function(e, i) { - if (typeof destination[i] === 'undefined') { - destination[i] = cloneIfNecessary(e, optionsArgument) - } else if (isMergeableObject(e)) { - destination[i] = deepmerge(target[i], e, optionsArgument) - } else if (target.indexOf(e) === -1) { - destination.push(cloneIfNecessary(e, optionsArgument)) - } - }) - return destination + return target.concat(source).map(function(element) { + return cloneUnlessOtherwiseSpecified(element, optionsArgument) + }) } function mergeObject(target, source, optionsArgument) { - var destination = {} - if (isMergeableObject(target)) { - Object.keys(target).forEach(function(key) { - destination[key] = cloneIfNecessary(target[key], optionsArgument) - }) - } - Object.keys(source).forEach(function(key) { - if (!isMergeableObject(source[key]) || !target[key]) { - destination[key] = cloneIfNecessary(source[key], optionsArgument) - } else { - destination[key] = deepmerge(target[key], source[key], optionsArgument) - } - }) - return destination + var destination = {} + if (isMergeableObject(target)) { + Object.keys(target).forEach(function(key) { + destination[key] = cloneUnlessOtherwiseSpecified(target[key], optionsArgument) + }) + } + Object.keys(source).forEach(function(key) { + if (!isMergeableObject(source[key]) || !target[key]) { + destination[key] = cloneUnlessOtherwiseSpecified(source[key], optionsArgument) + } else { + destination[key] = deepmerge(target[key], source[key], optionsArgument) + } + }) + return destination } function deepmerge(target, source, optionsArgument) { - var sourceIsArray = Array.isArray(source) - var targetIsArray = Array.isArray(target) - var options = optionsArgument || { arrayMerge: defaultArrayMerge } - var sourceAndTargetTypesMatch = sourceIsArray === targetIsArray + var sourceIsArray = Array.isArray(source) + var targetIsArray = Array.isArray(target) + var options = optionsArgument || { arrayMerge: defaultArrayMerge } + var sourceAndTargetTypesMatch = sourceIsArray === targetIsArray - if (!sourceAndTargetTypesMatch) { - return cloneIfNecessary(source, optionsArgument) - } else if (sourceIsArray) { - var arrayMerge = options.arrayMerge || defaultArrayMerge - return arrayMerge(target, source, optionsArgument) - } else { - return mergeObject(target, source, optionsArgument) - } + if (!sourceAndTargetTypesMatch) { + return cloneUnlessOtherwiseSpecified(source, optionsArgument) + } else if (sourceIsArray) { + var arrayMerge = options.arrayMerge || defaultArrayMerge + return arrayMerge(target, source, optionsArgument) + } else { + return mergeObject(target, source, optionsArgument) + } } deepmerge.all = function deepmergeAll(array, optionsArgument) { - if (!Array.isArray(array) || array.length < 2) { - throw new Error('first argument should be an array with at least two elements') - } + if (!Array.isArray(array)) { + throw new Error('first argument should be an array') + } - // we are sure there are at least 2 values, so it is safe to have no initial value - return array.reduce(function(prev, next) { - return deepmerge(prev, next, optionsArgument) - }) + return array.reduce(function(prev, next) { + return deepmerge(prev, next, optionsArgument) + }, {}) } module.exports = deepmerge diff --git a/package.json b/package.json index 7d4997c..9679fb1 100644 --- a/package.json +++ b/package.json @@ -18,13 +18,12 @@ }, "main": "dist/umd.js", "module": "dist/es.js", - "browser": "dist/cjs.js", "engines": { "node": ">=0.10.0" }, "scripts": { "build": "rollup -c", - "test": "npm run build && tap test/*.js && jsmd README.markdown" + "test": "npm run build && tap test/*.js && jsmd readme.md" }, "devDependencies": { "is-mergeable-object": "1.1.0", diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..e04788c --- /dev/null +++ b/readme.md @@ -0,0 +1,164 @@ +deepmerge +========= + +> ~554B gzipped, ~1.10KiB minified + +Merge the enumerable attributes of two objects deeply. + +example +======= + + + +```js +var x = { + foo: { bar: 3 }, + array: [{ + does: 'work', + too: [ 1, 2, 3 ] + }] +} + +var y = { + foo: { baz: 4 }, + quux: 5, + array: [{ + does: 'work', + too: [ 4, 5, 6 ] + }, { + really: 'yes' + }] +} + +var expected = { + foo: { + bar: 3, + baz: 4 + }, + array: [{ + does: 'work', + too: [ 1, 2, 3 ] + }, { + does: 'work', + too: [ 4, 5, 6 ] + }, { + really: 'yes' + }], + quux: 5 +} + +merge(x, y) // => expected +``` + +methods +======= + +``` +var merge = require('deepmerge') +``` + +merge(x, y, [options]) +----------- + +Merge two objects `x` and `y` deeply, returning a new merged object with the +elements from both `x` and `y`. + +If an element at the same key is present for both `x` and `y`, the value from +`y` will appear in the result. + +Merging creates a new object, so that neither `x` or `y` are be modified. + +merge.all(arrayOfObjects, [options]) +----------- + +Merges any number of objects into a single result object. + +```js +var x = { foo: { bar: 3 } } +var y = { foo: { baz: 4 } } +var z = { bar: 'yay!' } + +var expected = { foo: { bar: 3, baz: 4 }, bar: 'yay!' } + +merge.all([x, y, z]) // => expected +``` + +### options + +#### arrayMerge + +The merge will also concatenate arrays and merge array values by default. + +However, there are nigh-infinite valid ways to merge arrays, and you may want to supply your own. You can do this by passing an `arrayMerge` function as an option. + +```js +function overwriteMerge(destinationArray, sourceArray, options) { + return sourceArray +} +merge( + [1, 2, 3], + [3, 2, 1], + { arrayMerge: overwriteMerge } +) // => [3, 2, 1] +``` + +To prevent arrays from being merged: + +```js +const dontMerge = (destination, source) => source +const output = merge({ coolThing: [1,2,3] }, { coolThing: ['a', 'b', 'c'] }, { arrayMerge: dontMerge }) +output // => { coolThing: ['a', 'b', 'c'] } +``` + +To use the old (pre-version-2.0.0) array merging algorithm, pass in this function: + +```js +function oldArrayMerge(target, source, optionsArgument) { + var destination = target.slice() + source.forEach(function(e, i) { + if (typeof destination[i] === 'undefined') { + destination[i] = cloneIfNecessary(e, optionsArgument) + } else if (isMergeableObject(e)) { + destination[i] = deepmerge(target[i], e, optionsArgument) + } else if (target.indexOf(e) === -1) { + destination.push(cloneIfNecessary(e, optionsArgument)) + } + }) + return destination +} +``` + +#### clone + +*Deprecated.* + +Defaults to `true`. + +If `clone` is `false` then child objects will be copied directly instead of being cloned. This was the default behavior before version 2.x. + +install +======= + +With [npm](http://npmjs.org) do: + +```sh +npm install deepmerge +``` + +Just want to download the file without using any package managers/bundlers? [Download the UMD version from unpkg.com](https://unpkg.com/deepmerge/dist/umd.js). + +test +==== + +With [npm](http://npmjs.org) do: + +```sh +npm test +``` + +license +======= + +MIT diff --git a/rollup.config.js b/rollup.config.js index 125c194..6adb6c8 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -8,11 +8,10 @@ export default { name: 'deepmerge', plugins: [ commonjs(), - resolve() + resolve(), ], output: [ { file: pkg.main, format: 'umd' }, { file: pkg.module, format: 'es' }, - { file: pkg.browser, format: 'cjs' }, - ] + ], } diff --git a/test/custom-array-merge.js b/test/custom-array-merge.js index f339c17..5dbb61c 100644 --- a/test/custom-array-merge.js +++ b/test/custom-array-merge.js @@ -3,28 +3,24 @@ var test = require('tap').test test('custom merge array', function(t) { var mergeFunctionCalled = false - function concatMerge(target, source, options) { - t.notOk(mergeFunctionCalled) + function overwriteMerge(target, source, options) { mergeFunctionCalled = true + t.equal(options.arrayMerge, overwriteMerge) - t.deepEqual(target, [1, 2]) - t.deepEqual(source, [1, 2, 3]) - t.equal(options.arrayMerge, concatMerge) - - return target.concat(source) + return source } const destination = { - someArray: [1, 2], - someObject: { what: 'yes' } + someArray: [ 1, 2 ], + someObject: { what: 'yes' }, } const source = { - someArray: [1, 2, 3] + someArray: [ 1, 2, 3 ], } - const actual = merge(destination, source, { arrayMerge: concatMerge }) + const actual = merge(destination, source, { arrayMerge: overwriteMerge }) const expected = { - someArray: [1, 2, 1, 2, 3], - someObject: { what: 'yes' } + someArray: [ 1, 2, 3 ], + someObject: { what: 'yes' }, } t.ok(mergeFunctionCalled) @@ -33,11 +29,11 @@ test('custom merge array', function(t) { }) test('merge top-level arrays', function(t) { - function concatMerge(a, b) { - return a.concat(b) + function overwriteMerge(a, b) { + return b } - var actual = merge([1, 2], [1, 2], { arrayMerge: concatMerge }) - var expected = [1, 2, 1, 2] + var actual = merge([ 1, 2 ], [ 1, 2 ], { arrayMerge: overwriteMerge }) + var expected = [ 1, 2 ] t.deepEqual(actual, expected) t.end() diff --git a/test/merge-all.js b/test/merge-all.js index 40b21d3..5d64bd4 100644 --- a/test/merge-all.js +++ b/test/merge-all.js @@ -2,66 +2,88 @@ var merge = require('../') var test = require('tap').test test('throw error if first argument is not an array', function(t) { - t.throws(merge.all.bind(null, { example: true }, { another: '2' }), Error) - t.end() + t.throws(merge.all.bind(null, { example: true }, { another: '2' }), Error) + t.end() }) -test('throw error if first argument is an array with least than two elements', function(t) { - t.throws(merge.all.bind(null, [{ example: true }]), Error) - t.end() +test('return an empty object if first argument is an array with no elements', function(t) { + t.deepEqual(merge.all([]), {}) + t.end() +}) + +test('Work just fine if first argument is an array with least than two elements', function(t) { + var actual = merge.all([{ example: true }]) + var expected = { example: true } + t.deepEqual(actual, expected) + t.end() }) test('execute correctly if options object were not passed', function(t) { - var arrayToMerge = [{ example: true }, { another: '123' }] - t.doesNotThrow(merge.all.bind(null, arrayToMerge)) - t.end() + var arrayToMerge = [{ example: true }, { another: '123' }] + t.doesNotThrow(merge.all.bind(null, arrayToMerge)) + t.end() }) test('execute correctly if options object were passed', function(t) { - var arrayToMerge = [{ example: true }, { another: '123' }] - t.doesNotThrow(merge.all.bind(null, arrayToMerge, { clone:true })) - t.end() + var arrayToMerge = [{ example: true }, { another: '123' }] + t.doesNotThrow(merge.all.bind(null, arrayToMerge, { clone: true })) + t.end() }) test('invoke merge on every item in array should result with all props', function(t) { - var firstObject = { first: true } - var secondObject = { second: false } - var thirdObject = { third: 123 } - var fourthObject = { fourth: 'some string' } - - var mergedObject = merge.all([firstObject, secondObject, thirdObject, fourthObject]) - - t.ok(mergedObject.first === true) - t.ok(mergedObject.second === false) - t.ok(mergedObject.third === 123) - t.ok(mergedObject.fourth === 'some string') - t.end() + var firstObject = { first: true } + var secondObject = { second: false } + var thirdObject = { third: 123 } + var fourthObject = { fourth: 'some string' } + + var mergedObject = merge.all([ firstObject, secondObject, thirdObject, fourthObject ]) + + t.ok(mergedObject.first === true) + t.ok(mergedObject.second === false) + t.ok(mergedObject.third === 123) + t.ok(mergedObject.fourth === 'some string') + t.end() }) test('invoke merge on every item in array with clone should clone all elements', function(t) { - var firstObject = { a: { d: 123 } } - var secondObject = { b: { e: true } } - var thirdObject = { c: { f: 'string' } } + var firstObject = { a: { d: 123 } } + var secondObject = { b: { e: true } } + var thirdObject = { c: { f: 'string' } } + + var mergedWithClone = merge.all([ firstObject, secondObject, thirdObject ], { clone: true }) - var mergedWithClone = merge.all([firstObject, secondObject, thirdObject], { clone: true }) + t.notEqual(mergedWithClone.a, firstObject.a) + t.notEqual(mergedWithClone.b, secondObject.b) + t.notEqual(mergedWithClone.c, thirdObject.c) - t.notEqual(mergedWithClone.a, firstObject.a) - t.notEqual(mergedWithClone.b, secondObject.b) - t.notEqual(mergedWithClone.c, thirdObject.c) + t.end() +}) + +test('invoke merge on every item in array clone=false should not clone all elements', function(t) { + var firstObject = { a: { d: 123 } } + var secondObject = { b: { e: true } } + var thirdObject = { c: { f: 'string' } } + + var mergedWithoutClone = merge.all([ firstObject, secondObject, thirdObject ], { clone: false }) - t.end() + t.equal(mergedWithoutClone.a, firstObject.a) + t.equal(mergedWithoutClone.b, secondObject.b) + t.equal(mergedWithoutClone.c, thirdObject.c) + + t.end() }) -test('invoke merge on every item in array without clone should not clone all elements', function(t) { - var firstObject = { a: { d: 123 } } - var secondObject = { b: { e: true } } - var thirdObject = { c: { f: 'string' } } - var mergedWithoutClone = merge.all([firstObject, secondObject, thirdObject]) +test('invoke merge on every item in array without clone should clone all elements', function(t) { + var firstObject = { a: { d: 123 } } + var secondObject = { b: { e: true } } + var thirdObject = { c: { f: 'string' } } + + var mergedWithoutClone = merge.all([ firstObject, secondObject, thirdObject ]) - t.equal(mergedWithoutClone.a, firstObject.a) - t.equal(mergedWithoutClone.b, secondObject.b) - t.equal(mergedWithoutClone.c, thirdObject.c) + t.notEqual(mergedWithoutClone.a, firstObject.a) + t.notEqual(mergedWithoutClone.b, secondObject.b) + t.notEqual(mergedWithoutClone.c, thirdObject.c) - t.end() + t.end() }) diff --git a/test/merge.js b/test/merge.js index 66e566a..e03d2e9 100644 --- a/test/merge.js +++ b/test/merge.js @@ -2,538 +2,473 @@ var merge = require('../') var test = require('tap').test test('add keys in target that do not exist at the root', function(t) { - var src = { key1: 'value1', key2: 'value2' } - var target = {} + var src = { key1: 'value1', key2: 'value2' } + var target = {} - var res = merge(target, src) + var res = merge(target, src) - t.deepEqual(target, {}, 'merge should be immutable') - t.deepEqual(res, src) - t.end() + t.deepEqual(target, {}, 'merge should be immutable') + t.deepEqual(res, src) + t.end() }) test('merge existing simple keys in target at the roots', function(t) { - var src = { key1: 'changed', key2: 'value2' } - var target = { key1: 'value1', key3: 'value3' } - - var expected = { - key1: 'changed', - key2: 'value2', - key3: 'value3' - } - - t.deepEqual(target, { key1: 'value1', key3: 'value3' }) - t.deepEqual(merge(target, src), expected) - t.end() + var src = { key1: 'changed', key2: 'value2' } + var target = { key1: 'value1', key3: 'value3' } + + var expected = { + key1: 'changed', + key2: 'value2', + key3: 'value3', + } + + t.deepEqual(target, { key1: 'value1', key3: 'value3' }) + t.deepEqual(merge(target, src), expected) + t.end() }) test('merge nested objects into target', function(t) { - var src = { - key1: { - subkey1: 'changed', - subkey3: 'added' - } - } - var target = { - key1: { - subkey1: 'value1', - subkey2: 'value2' - } - } - - var expected = { - key1: { - subkey1: 'changed', - subkey2: 'value2', - subkey3: 'added' - } - } - - t.deepEqual(target, { - key1: { - subkey1: 'value1', - subkey2: 'value2' - } - }) - t.deepEqual(merge(target, src), expected) - t.end() + var src = { + key1: { + subkey1: 'changed', + subkey3: 'added', + }, + } + var target = { + key1: { + subkey1: 'value1', + subkey2: 'value2', + }, + } + + var expected = { + key1: { + subkey1: 'changed', + subkey2: 'value2', + subkey3: 'added', + }, + } + + t.deepEqual(target, { + key1: { + subkey1: 'value1', + subkey2: 'value2', + }, + }) + t.deepEqual(merge(target, src), expected) + t.end() }) test('replace simple key with nested object in target', function(t) { - var src = { - key1: { - subkey1: 'subvalue1', - subkey2: 'subvalue2' - } - } - var target = { - key1: 'value1', - key2: 'value2' - } - - var expected = { - key1: { - subkey1: 'subvalue1', - subkey2: 'subvalue2' - }, - key2: 'value2' - } - - t.deepEqual(target, { key1: 'value1', key2: 'value2' }) - t.deepEqual(merge(target, src), expected) - t.end() + var src = { + key1: { + subkey1: 'subvalue1', + subkey2: 'subvalue2', + }, + } + var target = { + key1: 'value1', + key2: 'value2', + } + + var expected = { + key1: { + subkey1: 'subvalue1', + subkey2: 'subvalue2', + }, + key2: 'value2', + } + + t.deepEqual(target, { key1: 'value1', key2: 'value2' }) + t.deepEqual(merge(target, src), expected) + t.end() }) test('should add nested object in target', function(t) { - var src = { - "b": { - "c": {} - } - } - - var target = { - "a": {} - } - - var expected = { - "a": {}, - "b": { - "c": {} - } - } - - t.deepEqual(merge(target, src), expected) - t.end() + var src = { + "b": { + "c": {}, + }, + } + + var target = { + "a": {}, + } + + var expected = { + "a": {}, + "b": { + "c": {}, + }, + } + + t.deepEqual(merge(target, src), expected) + t.end() }) test('should clone source and target', function(t) { - var src = { - "b": { - "c": "foo" - } - } - - var target = { - "a": { - "d": "bar" - } - } - - var expected = { - "a": { - "d": "bar" - }, - "b": { - "c": "foo" - } - } - - var merged = merge(target, src, { clone: true }) - - t.deepEqual(merged, expected) - - t.notEqual(merged.a, target.a) - t.notEqual(merged.b, src.b) - - t.end() + var src = { + "b": { + "c": "foo", + }, + } + + var target = { + "a": { + "d": "bar", + }, + } + + var expected = { + "a": { + "d": "bar", + }, + "b": { + "c": "foo", + }, + } + + var merged = merge(target, src, { clone: true }) + + t.deepEqual(merged, expected) + + t.notEqual(merged.a, target.a) + t.notEqual(merged.b, src.b) + + t.end() }) -test('should not clone source and target', function(t) { - var src = { - "b": { - "c": "foo" - } - } - - var target = { - "a": { - "d": "bar" - } - } - - var merged = merge(target, src) - t.equal(merged.a, target.a) - t.equal(merged.b, src.b) - - t.end() +test('should clone source and target', function(t) { + var src = { + "b": { + "c": "foo", + }, + } + + var target = { + "a": { + "d": "bar", + }, + } + + var merged = merge(target, src) + t.notEqual(merged.a, target.a) + t.notEqual(merged.b, src.b) + + t.end() }) test('should replace object with simple key in target', function(t) { - var src = { key1: 'value1' } - var target = { - key1: { - subkey1: 'subvalue1', - subkey2: 'subvalue2' - }, - key2: 'value2' - } - - var expected = { key1: 'value1', key2: 'value2' } - - t.deepEqual(target, { - key1: { - subkey1: 'subvalue1', - subkey2: 'subvalue2' - }, - key2: 'value2' - }) - t.deepEqual(merge(target, src), expected) - t.end() + var src = { key1: 'value1' } + var target = { + key1: { + subkey1: 'subvalue1', + subkey2: 'subvalue2', + }, + key2: 'value2', + } + + var expected = { key1: 'value1', key2: 'value2' } + + t.deepEqual(target, { + key1: { + subkey1: 'subvalue1', + subkey2: 'subvalue2', + }, + key2: 'value2', + }) + t.deepEqual(merge(target, src), expected) + t.end() }) test('should replace objects with arrays', function(t) { - var target = [ - { key1: { subkey: 'one' } } - ] + var target = { key1: { subkey: 'one' } } - var src = [ - { key1: [ 'subkey' ] } - ] + var src = { key1: [ 'subkey' ] } - var expected = [ - { key1: [ 'subkey' ] } - ] + var expected = { key1: [ 'subkey' ] } - t.deepEqual(merge(target, src), expected) - t.end() + t.deepEqual(merge(target, src), expected) + t.end() }) test('should replace arrays with objects', function(t) { - var target = { key1: [ "subkey" ] } + var target = { key1: [ "subkey" ] } - var src = { key1: { subkey: 'one' } } + var src = { key1: { subkey: 'one' } } - var expected = { key1: { subkey: 'one' } } + var expected = { key1: { subkey: 'one' } } - t.deepEqual(merge(target, src), expected) - t.end() + t.deepEqual(merge(target, src), expected) + t.end() }) test('should replace dates with arrays', function(t) { - var target = [ - { key1: new Date() } - ] + var target = { key1: new Date() } - var src = [ - { key1: [ "subkey" ] } - ] + var src = { key1: [ "subkey" ] } - var expected = [ - { key1: [ "subkey" ] } - ] + var expected = { key1: [ "subkey" ] } - t.deepEqual(merge(target, src), expected) - t.end() + t.deepEqual(merge(target, src), expected) + t.end() }) test('should replace null with arrays', function(t) { - var target = { - key1: null - } + var target = { + key1: null, + } - var src = { - key1: [ "subkey" ] - } + var src = { + key1: [ "subkey" ], + } - var expected = { - key1: [ "subkey" ] - } + var expected = { + key1: [ "subkey" ], + } - t.deepEqual(merge(target, src), expected) - t.end() + t.deepEqual(merge(target, src), expected) + t.end() }) test('should work on simple array', function(t) { - var src = [ 'one', 'three' ] - var target = [ 'one', 'two' ] + var src = [ 'one', 'three' ] + var target = [ 'one', 'two' ] - var expected = [ 'one', 'two', 'three' ] + var expected = [ 'one', 'two', 'one', 'three' ] - t.deepEqual(target, [ 'one', 'two' ]) - t.deepEqual(merge(target, src), expected) - t.ok(Array.isArray(merge(target, src))) - t.end() + t.deepEqual(merge(target, src), expected) + t.ok(Array.isArray(merge(target, src))) + t.end() }) test('should work on another simple array', function(t) { - var target = [ "a1", "a2", "c1", "f1", "p1" ] - var src = [ "t1", "s1", "c2", "r1", "p2", "p3" ] - - var expected = [ "a1", "a2", "c1", "f1", "p1", "t1", "s1", "c2", "r1", "p2", "p3" ] - t.deepEqual(target, [ "a1", "a2", "c1", "f1", "p1" ]) - t.deepEqual(merge(target, src), expected) - t.ok(Array.isArray(merge(target, src))) - t.end() + var target = [ "a1", "a2", "c1", "f1", "p1" ] + var src = [ "t1", "s1", "c2", "r1", "p2", "p3" ] + + var expected = [ "a1", "a2", "c1", "f1", "p1", "t1", "s1", "c2", "r1", "p2", "p3" ] + t.deepEqual(target, [ "a1", "a2", "c1", "f1", "p1" ]) + t.deepEqual(merge(target, src), expected) + t.ok(Array.isArray(merge(target, src))) + t.end() }) test('should work on array properties', function(t) { - var src = { - key1: [ 'one', 'three' ], - key2: [ 'four' ] - } - var target = { - key1: [ 'one', 'two' ] - } - - var expected = { - key1: [ 'one', 'two', 'three' ], - key2: [ 'four' ] - } - - t.deepEqual(target, { - key1: [ 'one', 'two' ] - }) - - t.deepEqual(merge(target, src), expected) - t.ok(Array.isArray(merge(target, src).key1)) - t.ok(Array.isArray(merge(target, src).key2)) - t.end() + var src = { + key1: [ 'one', 'three' ], + key2: [ 'four' ], + } + var target = { + key1: [ 'one', 'two' ], + } + + var expected = { + key1: [ 'one', 'two', 'one', 'three' ], + key2: [ 'four' ], + } + + t.deepEqual(merge(target, src), expected) + t.ok(Array.isArray(merge(target, src).key1)) + t.ok(Array.isArray(merge(target, src).key2)) + t.end() }) test('should work on array properties with clone option', function(t) { - var src = { - key1: [ 'one', 'three' ], - key2: [ 'four' ] - } - var target = { - key1: [ 'one', 'two' ] - } - - t.deepEqual(target, { - key1: [ 'one', 'two' ] - }) - var merged = merge(target, src, { clone: true }) - t.notEqual(merged.key1, src.key1) - t.notEqual(merged.key1, target.key1) - t.notEqual(merged.key2, src.key2) - t.end() + var src = { + key1: [ 'one', 'three' ], + key2: [ 'four' ], + } + var target = { + key1: [ 'one', 'two' ], + } + + t.deepEqual(target, { + key1: [ 'one', 'two' ], + }) + var merged = merge(target, src, { clone: true }) + t.notEqual(merged.key1, src.key1) + t.notEqual(merged.key1, target.key1) + t.notEqual(merged.key2, src.key2) + t.end() }) test('should work on array of objects', function(t) { - var src = [ - { key1: [ 'one', 'three' ], key2: [ 'one' ] }, - { key3: [ 'five' ] } - ] - var target = [ - { key1: [ 'one', 'two' ] }, - { key3: [ 'four' ] } - ] - - var expected = [ - { key1: [ 'one', 'two', 'three' ], key2: [ 'one' ] }, - { key3: [ 'four', 'five' ] } - ] - - t.deepEqual(target, [ - { key1: [ 'one', 'two' ] }, - { key3: [ 'four' ] } - ]) - t.deepEqual(merge(target, src), expected) - t.ok(Array.isArray(merge(target, src)), 'result should be an array') - t.ok(Array.isArray(merge(target, src)[0].key1), 'subkey should be an array too') - - t.end() + var src = [ + { key1: [ 'one', 'three' ], key2: [ 'one' ] }, + { key3: [ 'five' ] }, + ] + var target = [ + { key1: [ 'one', 'two' ] }, + { key3: [ 'four' ] }, + ] + + var expected = [ + { key1: [ 'one', 'two' ] }, + { key3: [ 'four' ] }, + { key1: [ 'one', 'three' ], key2: [ 'one' ] }, + { key3: [ 'five' ] }, + ] + + t.deepEqual(merge(target, src), expected) + t.ok(Array.isArray(merge(target, src)), 'result should be an array') + t.ok(Array.isArray(merge(target, src)[0].key1), 'subkey should be an array too') + + t.end() }) test('should work on array of objects with clone option', function(t) { - var src = [ - { key1: [ 'one', 'three' ], key2: [ 'one' ] }, - { key3: [ 'five' ] } - ] - var target = [ - { key1: [ 'one', 'two' ] }, - { key3: [ 'four' ] } - ] - - var expected = [ - { key1: [ 'one', 'two', 'three' ], key2: [ 'one' ] }, - { key3: [ 'four', 'five' ] } - ] - - t.deepEqual(target, [ - { key1: [ 'one', 'two' ] }, - { key3: [ 'four' ] } - ]) - var merged = merge(target, src, { clone: true }) - t.deepEqual(merged, expected) - t.ok(Array.isArray(merge(target, src)), 'result should be an array') - t.ok(Array.isArray(merge(target, src)[0].key1), 'subkey should be an array too') - t.notEqual(merged[0].key1, src[0].key1) - t.notEqual(merged[0].key1, target[0].key1) - t.notEqual(merged[0].key2, src[0].key2) - t.notEqual(merged[1].key3, src[1].key3) - t.notEqual(merged[1].key3, target[1].key3) - t.end() -}) - -test('should work on arrays of nested objects', function(t) { - var target = [ - { key1: { subkey: 'one' } } - ] - - var src = [ - { key1: { subkey: 'two' } }, - { key2: { subkey: 'three' } } - ] - - var expected = [ - { key1: { subkey: 'two' } }, - { key2: { subkey: 'three' } } - ] - - t.deepEqual(merge(target, src), expected) - t.end() + var src = [ + { key1: [ 'one', 'three' ], key2: [ 'one' ] }, + { key3: [ 'five' ] }, + ] + var target = [ + { key1: [ 'one', 'two' ] }, + { key3: [ 'four' ] }, + ] + + var expected = [ + { key1: [ 'one', 'two' ] }, + { key3: [ 'four' ] }, + { key1: [ 'one', 'three' ], key2: [ 'one' ] }, + { key3: [ 'five' ] }, + ] + + var merged = merge(target, src, { clone: true }) + t.deepEqual(merged, expected) + t.ok(Array.isArray(merge(target, src)), 'result should be an array') + t.ok(Array.isArray(merge(target, src)[0].key1), 'subkey should be an array too') + t.notEqual(merged[0].key1, src[0].key1) + t.notEqual(merged[0].key1, target[0].key1) + t.notEqual(merged[0].key2, src[0].key2) + t.notEqual(merged[1].key3, src[1].key3) + t.notEqual(merged[1].key3, target[1].key3) + t.end() }) test('should treat regular expressions like primitive values', function(t) { - var target = { key1: /abc/ } - var src = { key1: /efg/ } - var expected = { key1: /efg/ } + var target = { key1: /abc/ } + var src = { key1: /efg/ } + var expected = { key1: /efg/ } - t.deepEqual(merge(target, src), expected) - t.deepEqual(merge(target, src).key1.test('efg'), true) - t.end() + t.deepEqual(merge(target, src), expected) + t.deepEqual(merge(target, src).key1.test('efg'), true) + t.end() }) test('should treat regular expressions like primitive values and should not' - + ' clone even with clone option', function(t) { - - var target = { key1: /abc/ } - var src = { key1: /efg/ } + + ' clone even with clone option', function(t) { + var target = { key1: /abc/ } + var src = { key1: /efg/ } - var output = merge(target, src, { clone: true }) + var output = merge(target, src, { clone: true }) - t.equal(output.key1, src.key1) - t.end() + t.equal(output.key1, src.key1) + t.end() } ) test('should treat dates like primitives', function(t) { - var monday = new Date('2016-09-27T01:08:12.761Z') - var tuesday = new Date('2016-09-28T01:18:12.761Z') - - var target = { - key: monday - } - var source = { - key: tuesday - } - - var expected = { - key: tuesday - } - var actual = merge(target, source) - - t.deepEqual(actual, expected) - t.equal(actual.key.valueOf(), tuesday.valueOf()) - t.end() + var monday = new Date('2016-09-27T01:08:12.761Z') + var tuesday = new Date('2016-09-28T01:18:12.761Z') + + var target = { + key: monday, + } + var source = { + key: tuesday, + } + + var expected = { + key: tuesday, + } + var actual = merge(target, source) + + t.deepEqual(actual, expected) + t.equal(actual.key.valueOf(), tuesday.valueOf()) + t.end() }) test('should treat dates like primitives and should not clone even with clone' - + ' option', function(t) { - var monday = new Date('2016-09-27T01:08:12.761Z') - var tuesday = new Date('2016-09-28T01:18:12.761Z') + + ' option', function(t) { + var monday = new Date('2016-09-27T01:08:12.761Z') + var tuesday = new Date('2016-09-28T01:18:12.761Z') - var target = { - key: monday - } - var source = { - key: tuesday - } + var target = { + key: monday, + } + var source = { + key: tuesday, + } - var actual = merge(target, source, { clone: true }) + var actual = merge(target, source, { clone: true }) - t.equal(actual.key, tuesday) - t.end() + t.equal(actual.key, tuesday) + t.end() }) test('should work on array with null in it', function(t) { - var target = [] + var target = [] - var src = [ null ] + var src = [ null ] - var expected = [ null ] + var expected = [ null ] - t.deepEqual(merge(target, src), expected) - t.end() + t.deepEqual(merge(target, src), expected) + t.end() }) test('should clone array\'s element if it is object', function(t) { - var a = { key: 'yup' } - var target = [] - var source = [ a ] + var a = { key: 'yup' } + var target = [] + var source = [ a ] - var output = merge(target, source, { clone: true }) + var output = merge(target, source, { clone: true }) - t.notEqual(output[0], a) - t.equal(output[0].key, 'yup') - t.end() + t.notEqual(output[0], a) + t.equal(output[0].key, 'yup') + t.end() }) test('should clone an array property when there is no target array', function(t) { - const someObject = {} - var target = {} - var source = { ary: [ someObject ] } - var output = merge(target, source, { clone: true }) - - t.deepEqual(output, { ary: [{}] }) - t.notEqual(output.ary[0], someObject) - t.end() + const someObject = {} + var target = {} + var source = { ary: [ someObject ] } + var output = merge(target, source, { clone: true }) + + t.deepEqual(output, { ary: [{}] }) + t.notEqual(output.ary[0], someObject) + t.end() }) test('should overwrite values when property is initialised but undefined', function(t) { - var target1 = { value: [] } - var target2 = { value: null } - var target3 = { value: 2 } - - var src = { value: undefined } - - function hasUndefinedProperty(o) { - t.ok(o.hasOwnProperty('value')) - t.type(o.value, 'undefined') - } - - hasUndefinedProperty(merge(target1, src)) - hasUndefinedProperty(merge(target2, src)) - hasUndefinedProperty(merge(target3, src)) - - t.end() -}) - -test('null should be equal to null in an array', function(t) { - var target = [ null, 'dude' ] - var source = [ null, 'lol' ] - - var expected = [ null, 'dude', 'lol' ] - var actual = merge(target, source) - - t.deepEqual(actual, expected) - t.end() -}) + var target1 = { value: [] } + var target2 = { value: null } + var target3 = { value: 2 } -test('dates in an array should be compared correctly', function(t) { - var monday = new Date('2016-09-27T01:08:12.761Z') + var src = { value: undefined } - var target = [ monday, 'dude' ] - var source = [ monday, 'lol' ] + function hasUndefinedProperty(o) { + t.ok(o.hasOwnProperty('value')) + t.type(o.value, 'undefined') + } - var expected = [ monday, 'dude', 'lol' ] - var actual = merge(target, source) + hasUndefinedProperty(merge(target1, src)) + hasUndefinedProperty(merge(target2, src)) + hasUndefinedProperty(merge(target3, src)) - t.deepEqual(actual, expected) - t.end() + t.end() }) test('dates should copy correctly in an array', function(t) { - var monday = new Date('2016-09-27T01:08:12.761Z') - var tuesday = new Date('2016-09-28T01:18:12.761Z') + var monday = new Date('2016-09-27T01:08:12.761Z') + var tuesday = new Date('2016-09-28T01:18:12.761Z') - var target = [ monday, 'dude' ] - var source = [ tuesday, 'lol' ] + var target = [ monday, 'dude' ] + var source = [ tuesday, 'lol' ] - var expected = [ monday, 'dude', tuesday, 'lol' ] - var actual = merge(target, source) + var expected = [ monday, 'dude', tuesday, 'lol' ] + var actual = merge(target, source) - t.deepEqual(actual, expected) - t.end() + t.deepEqual(actual, expected) + t.end() })