diff --git a/package.json b/package.json index 67d0acdf..b0b646e3 100644 --- a/package.json +++ b/package.json @@ -99,6 +99,7 @@ "@semantic-release/github": "^8.0.2", "@semantic-release/npm": "^9.0.0", "@semantic-release/release-notes-generator": "^10.0.3", + "@types/lodash": "^4.14.178", "@types/node": "^17.0.16", "@types/rollup-plugin-auto-external": "^2.0.2", "@typescript-eslint/eslint-plugin": "^5.11.0", @@ -128,6 +129,7 @@ "eslint-plugin-unicorn": "^40.1.0", "husky": "^7.0.4", "lint-staged": "^12.3.3", + "lodash": "^4.17.21", "markdownlint-cli": "^0.31.1", "marked": "^4.0.12", "nyc": "^15.1.0", diff --git a/src/deepmerge.ts b/src/deepmerge.ts index f2638b58..97a24787 100644 --- a/src/deepmerge.ts +++ b/src/deepmerge.ts @@ -123,13 +123,6 @@ export function deepmergeCustom< * The customized deepmerge function. */ function customizedDeepmerge(...objects: ReadonlyArray) { - if (objects.length === 0) { - return undefined; - } - if (objects.length === 1) { - return objects[0]; - } - return mergeUnknowns< ReadonlyArray, typeof utils, @@ -186,6 +179,17 @@ function mergeUnknowns< M, MM extends Readonly> >(values: Ts, utils: U, meta: M | undefined): DeepMergeHKT { + if (values.length === 0) { + return undefined as DeepMergeHKT; + } + if (values.length === 1) { + return utils.mergeFunctions.mergeOthers( + values, + utils, + meta + ) as DeepMergeHKT; + } + const type = getObjectType(values[0]); // eslint-disable-next-line functional/no-conditional-statement -- add an early escape for better performance. @@ -274,14 +278,11 @@ function mergeRecords< parents: values, } as unknown as MM); - result[key] = - propValues.length === 1 - ? propValues[0] - : mergeUnknowns, U, MF, M, MM>( - propValues, - utils, - updatedMeta - ); + result[key] = mergeUnknowns, U, MF, M, MM>( + propValues, + utils, + updatedMeta + ); } /* eslint-enable functional/no-loop-statement, functional/no-conditional-statement */ diff --git a/tests/deepmerge-custom.test.ts b/tests/deepmerge-custom.test.ts index f3bbd534..5449cfe6 100644 --- a/tests/deepmerge-custom.test.ts +++ b/tests/deepmerge-custom.test.ts @@ -1,6 +1,7 @@ /* eslint-disable @typescript-eslint/consistent-type-definitions, @typescript-eslint/no-unused-vars */ import test from "ava"; +import _ from "lodash"; import { deepmergeCustom } from "@/deepmerge"; import type { @@ -563,3 +564,29 @@ test("custom merge with parents", (t) => { t.deepEqual(merged, expected); }); + +test("custom merge that clones", (t) => { + const x = { foo: { bar: { baz: { qux: [1, 2, 3] } } } }; + const y = { bar: new Date("2021-02-02"), baz: { qux: 1 } }; + + const expected = { + foo: _.cloneDeep(x.foo), + bar: _.cloneDeep(y.bar), + baz: _.cloneDeep(y.baz), + } as const; + + const customizedDeepmerge = deepmergeCustom({ + mergeOthers: (values, utils) => + _.cloneDeep(utils.defaultMergeFunctions.mergeOthers(values)), + }); + + const merged = customizedDeepmerge(x, y); + + t.deepEqual(merged, expected); + t.not(merged.foo, x.foo); + t.not(merged.foo.bar, x.foo.bar); + t.not(merged.foo.bar.baz, x.foo.bar.baz); + t.not(merged.foo.bar.baz.qux, x.foo.bar.baz.qux); + t.not(merged.bar, y.bar); + t.not(merged.baz, y.baz); +}); diff --git a/yarn.lock b/yarn.lock index df6dcb84..fdd0bd64 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1280,6 +1280,11 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= +"@types/lodash@^4.14.178": + version "4.14.178" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.178.tgz#341f6d2247db528d4a13ddbb374bcdc80406f4f8" + integrity sha512-0d5Wd09ItQWH1qFbEyQ7oTQ3GZrMfth5JkbN3EvTKLXcHLRDSXeLnlvlOn0wvxVIwK5o2M8JzP/OWz7T3NRsbw== + "@types/mdast@^3.0.0": version "3.0.9" resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.9.tgz#3f7fa18faf9e567da9aa49e44ecc76ad33c359ce"