diff --git a/package-lock.json b/package-lock.json index 114b1db..6f16f83 100644 --- a/package-lock.json +++ b/package-lock.json @@ -283,6 +283,12 @@ "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==", "dev": true }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "dev": true + }, "@types/node": { "version": "8.10.54", "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.54.tgz", @@ -295,6 +301,15 @@ "integrity": "sha512-M56NfQHfaWuaj6daSgCVs7jh8fXLI3LmxjRoQxmOvYesgIkI+9HPsDLO0vd7wX7cwA0D0ZWFEJdp0VPwLdS+bQ==", "dev": true }, + "@types/tape": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@types/tape/-/tape-4.13.0.tgz", + "integrity": "sha512-0V8cKowBdsiA9nbxAg7531sF2cdPZNiUogcfIUeUGm+bejUBE/bvibz3rH36iQP9bQjO/sOzFwU97/uC5mCyoA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@typescript-eslint/eslint-plugin": { "version": "4.8.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.8.0.tgz", @@ -487,6 +502,12 @@ "readable-stream": "^2.0.6" } }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -1918,12 +1939,6 @@ "integrity": "sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw==", "dev": true }, - "is-mergeable-object": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-mergeable-object/-/is-mergeable-object-1.1.0.tgz", - "integrity": "sha512-JfyDDwUdtS4yHCgUpxOyKB9dnfZ0gecufxB0eytX6BmSXSE+8dbxDGt+V7CNRIRJ9sYFV/WQt2KJG6hNob2sBw==", - "dev": true - }, "is-module": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", @@ -2129,6 +2144,15 @@ "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", "dev": true }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, "jsonfile": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", @@ -2181,6 +2205,12 @@ "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", "dev": true }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, "marked": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/marked/-/marked-0.7.0.tgz", @@ -2809,8 +2839,17 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", "dev": true, - "optional": true + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } }, "sourcemap-codec": { "version": "1.4.6", @@ -2950,6 +2989,12 @@ "ansi-regex": "^2.0.0" } }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", @@ -3161,6 +3206,39 @@ "punycode": "^2.1.1" } }, + "ts-node": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.0.0.tgz", + "integrity": "sha512-/TqB4SnererCDR/vb4S/QvSZvzQMJN8daAslg7MeaiHvD8rDZsSfXmNeNumyZZzMned72Xoq/isQljYSt8Ynfg==", + "dev": true, + "requires": { + "arg": "^4.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.17", + "yn": "3.1.1" + }, + "dependencies": { + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + } + } + }, + "tsconfig-paths": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", + "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==", + "dev": true, + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.0", + "strip-bom": "^3.0.0" + } + }, "tslib": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", @@ -3554,6 +3632,12 @@ "camelcase": "^5.0.0", "decamelize": "^1.2.0" } + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true } } } diff --git a/package.json b/package.json index 7b75bb7..4b43802 100644 --- a/package.json +++ b/package.json @@ -60,8 +60,8 @@ "presize": "npm run build:js", "size": "terser --compress --mangle -- ./dist/index.umd.js | gzip -c | wc -c", "test": "npm run test:js && npm run test:types && npm run test:jsmd", - "pretest:js": "npm run build:js", - "test:js": "tape test/*.js", + "test:js": "ts-node -C typescript -P tsconfig.test.json -r tsconfig-paths/register node_modules/tape/bin/tape test/*.ts", + "pretest:jsmd": "npm run build:js", "test:jsmd": "jsmd readme.md", "pretest:types": "npm run build:types", "test:types": "dtslint --localTs node_modules/typescript/lib --expectOnly test/types" @@ -72,17 +72,19 @@ "@rollup/plugin-node-resolve": "^10.0.0", "@rollup/plugin-typescript": "^6.1.0", "@types/node": "^8.10.54", + "@types/tape": "^4.13.0", "@typescript-eslint/eslint-plugin": "^4.8.0", "@typescript-eslint/parser": "^4.8.0", "dtslint": "^4.0.5", "eslint": "^7.13.0", - "is-mergeable-object": "1.1.0", "is-plain-obj": "^3.0.0", "jsmd": "^1.0.2", "rimraf": "^3.0.2", "rollup": "^2.33.1", "tape": "^5.0.1", "terser": "^5.3.8", + "ts-node": "^9.0.0", + "tsconfig-paths": "^3.9.0", "tslib": "^2.0.3", "typescript": "^4.1.2" }, diff --git a/src/options.ts b/src/options.ts index 318ae94..3a77b14 100644 --- a/src/options.ts +++ b/src/options.ts @@ -1,7 +1,7 @@ import isPlainObj from "is-plain-obj" import { cloneUnlessOtherwiseSpecified } from "./impl" -import type { Property } from "./types" +import type { FlattenAlias, Property } from "./types" /** * Deep merge options. @@ -23,7 +23,7 @@ export type ExplicitOptions = { /** * Deep merge options with defaults applied. */ -export type FullOptions = { +export type FullOptions = FlattenAlias<{ arrayMerge: O[`arrayMerge`] extends undefined ? typeof defaultArrayMerge : NonNullable @@ -33,7 +33,7 @@ export type FullOptions = { ? typeof defaultIsMergeable : NonNullable cloneUnlessOtherwiseSpecified: (value: T, options: FullOptions) => T -} +}> /** * A function that determins if a type is mergable. @@ -85,5 +85,5 @@ export function getFullOptions(options?: O): FullOptions { clone: true, ...overrides, cloneUnlessOtherwiseSpecified, - } as FullOptions + } as unknown as FullOptions } diff --git a/src/types.ts b/src/types.ts index f298a09..3775307 100644 --- a/src/types.ts +++ b/src/types.ts @@ -35,7 +35,7 @@ type DeepMergeValues = And, IsArray> /** * Deep merge 2 non-array objects. */ -export type DeepMergeObjects = FlatternAlias< +export type DeepMergeObjects = FlattenAlias< // @see https://github.com/microsoft/TypeScript/issues/41448 { -readonly [K in keyof T1]: DeepMergeObjectProps, ValueOfKey, O> @@ -118,7 +118,7 @@ type MaybeLeaf = Or< * single object. */ // eslint-disable-next-line @typescript-eslint/ban-types -type FlatternAlias = {} & { [P in keyof T]: T[P] } +export type FlattenAlias = {} & { [P in keyof T]: T[P] } /** * Get the value of the given key in the given object. diff --git a/test/custom-array-merge.js b/test/custom-array-merge.ts similarity index 63% rename from test/custom-array-merge.js rename to test/custom-array-merge.ts index 86c7299..35cc854 100644 --- a/test/custom-array-merge.js +++ b/test/custom-array-merge.ts @@ -1,9 +1,10 @@ -const merge = require(`../`).default -const test = require(`tape`) +import type { Options } from "deepmerge" +import deepmerge from "deepmerge" +import test from "tape" test(`custom merge array`, (t) => { let mergeFunctionCalled = false - function overwriteMerge(target, source, options) { + const overwriteMerge: Options[`arrayMerge`] = (target, source, options) => { mergeFunctionCalled = true t.equal(options.arrayMerge, overwriteMerge) @@ -17,7 +18,7 @@ test(`custom merge array`, (t) => { someArray: [ 1, 2, 3 ], } - const actual = merge(destination, source, { arrayMerge: overwriteMerge }) + const actual = deepmerge(destination, source, { arrayMerge: overwriteMerge }) const expected = { someArray: [ 1, 2, 3 ], someObject: { what: `yes` }, @@ -29,10 +30,8 @@ test(`custom merge array`, (t) => { }) test(`merge top-level arrays`, (t) => { - function overwriteMerge(a, b) { - return b - } - const actual = merge([ 1, 2 ], [ 1, 2 ], { arrayMerge: overwriteMerge }) + const overwriteMerge: Options[`arrayMerge`] = (a, b) => b + const actual = deepmerge([ 1, 2 ], [ 1, 2 ], { arrayMerge: overwriteMerge }) const expected = [ 1, 2 ] t.deepEqual(actual, expected) @@ -41,7 +40,7 @@ test(`merge top-level arrays`, (t) => { test(`cloner function is available for merge functions to use`, (t) => { let customMergeWasCalled = false - function cloneMerge(target, source, options) { + const cloneMerge: Options[`arrayMerge`] = (target, source, options) => { customMergeWasCalled = true t.ok(options.cloneUnlessOtherwiseSpecified, `cloner function is available`) return target.concat(source).map((element) => options.cloneUnlessOtherwiseSpecified(element, options)) @@ -60,9 +59,9 @@ test(`cloner function is available for merge functions to use`, (t) => { key2: [ `four` ], } - t.deepEqual(merge(target, src, { arrayMerge: cloneMerge }), expected) + t.deepEqual(deepmerge(target, src, { arrayMerge: cloneMerge }), expected) t.ok(customMergeWasCalled) - t.ok(Array.isArray(merge(target, src).key1)) - t.ok(Array.isArray(merge(target, src).key2)) + t.ok(Array.isArray(deepmerge(target, src).key1)) + t.ok(Array.isArray(deepmerge(target, src).key2)) t.end() }) diff --git a/test/custom-is-mergeable-object.js b/test/custom-is-mergeable-object.ts similarity index 62% rename from test/custom-is-mergeable-object.js rename to test/custom-is-mergeable-object.ts index 5025e61..e3b4de4 100644 --- a/test/custom-is-mergeable-object.js +++ b/test/custom-is-mergeable-object.ts @@ -1,15 +1,15 @@ -const merge = require(`../`).default -const test = require(`tape`) +import type { Options } from "deepmerge" +import deepmerge from "deepmerge" +import test from "tape" test(`isMergeable function copying object over object`, (t) => { const src = { key: { isMergeable: false }, baz: `yes` } const target = { key: { foo: `wat` }, baz: `whatever` } - function customIsMergeable(object) { - return object && typeof value === `object` && object.isMergeable !== false - } + const customIsMergeable: Options[`isMergeable`] = (object) => + object && typeof object === `object` && object.isMergeable !== false - const res = merge(target, src, { + const res = deepmerge(target, src, { isMergeable: customIsMergeable, }) @@ -22,11 +22,10 @@ test(`isMergeable function copying object over nothing`, (t) => { const src = { key: { isMergeable: false, foo: `bar` }, baz: `yes` } const target = { baz: `whatever` } - function customIsMergeable(object) { - return object && typeof value === `object` && object.isMergeable !== false - } + const customIsMergeable: Options[`isMergeable`] = (object) => + object && typeof object === `object` && object.isMergeable !== false - const res = merge(target, src, { + const res = deepmerge(target, src, { isMergeable: customIsMergeable, }) diff --git a/test/merge-all.js b/test/merge-all.ts similarity index 68% rename from test/merge-all.js rename to test/merge-all.ts index 9108874..5f7b004 100644 --- a/test/merge-all.js +++ b/test/merge-all.ts @@ -1,18 +1,20 @@ -const mergeAll = require(`../`).deepmergeAll -const test = require(`tape`) +import { deepmergeAll } from "deepmerge" +import test from "tape" test(`throw error if first argument is not an array`, (t) => { - t.throws(mergeAll.bind(null, { example: true }, { another: `2` }), Error) + // eslint-disable-next-line @typescript-eslint/ban-ts-comment, @typescript-eslint/prefer-ts-expect-error -- FIXME: Can't use @ts-expect-error due to it failing in tests. + // @ts-ignore Expect a type error when calling the function incorrectly. + t.throws(deepmergeAll.bind(null, { example: true }, { another: `2` }), Error) t.end() }) test(`return an empty object if first argument is an array with no elements`, (t) => { - t.deepEqual(mergeAll([]), {}) + t.deepEqual(deepmergeAll([]), {}) t.end() }) test(`Work just fine if first argument is an array with least than two elements`, (t) => { - const actual = mergeAll([{ example: true }]) + const actual = deepmergeAll([{ example: true }]) const expected = { example: true } t.deepEqual(actual, expected) t.end() @@ -20,13 +22,13 @@ test(`Work just fine if first argument is an array with least than two elements` test(`execute correctly if options object were not passed`, (t) => { const arrayToMerge = [{ example: true }, { another: `123` }] - t.doesNotThrow(mergeAll.bind(null, arrayToMerge)) + t.doesNotThrow(deepmergeAll.bind(null, arrayToMerge)) t.end() }) test(`execute correctly if options object were passed`, (t) => { const arrayToMerge = [{ example: true }, { another: `123` }] - t.doesNotThrow(mergeAll.bind(null, arrayToMerge, { clone: true })) + t.doesNotThrow(deepmergeAll.bind(null, arrayToMerge, { clone: true })) t.end() }) @@ -36,7 +38,7 @@ test(`invoke merge on every item in array should result with all props`, (t) => const thirdObject = { third: 123 } const fourthObject = { fourth: `some string` } - const mergedObject = mergeAll([ firstObject, secondObject, thirdObject, fourthObject ]) + const mergedObject = deepmergeAll([ firstObject, secondObject, thirdObject, fourthObject ]) t.ok(mergedObject.first === true) t.ok(mergedObject.second === false) @@ -50,7 +52,7 @@ test(`invoke merge on every item in array with clone should clone all elements`, const secondObject = { b: { e: true } } const thirdObject = { c: { f: `string` } } - const mergedWithClone = mergeAll([ firstObject, secondObject, thirdObject ], { clone: true }) + const mergedWithClone = deepmergeAll([ firstObject, secondObject, thirdObject ], { clone: true }) t.notEqual(mergedWithClone.a, firstObject.a) t.notEqual(mergedWithClone.b, secondObject.b) @@ -64,7 +66,7 @@ test(`invoke merge on every item in array clone=false should not clone all eleme const secondObject = { b: { e: true } } const thirdObject = { c: { f: `string` } } - const mergedWithoutClone = mergeAll([ firstObject, secondObject, thirdObject ], { clone: false }) + const mergedWithoutClone = deepmergeAll([ firstObject, secondObject, thirdObject ], { clone: false }) t.equal(mergedWithoutClone.a, firstObject.a) t.equal(mergedWithoutClone.b, secondObject.b) @@ -79,7 +81,7 @@ test(`invoke merge on every item in array without clone should clone all element const secondObject = { b: { e: true } } const thirdObject = { c: { f: `string` } } - const mergedWithoutClone = mergeAll([ firstObject, secondObject, thirdObject ]) + const mergedWithoutClone = deepmergeAll([ firstObject, secondObject, thirdObject ]) t.notEqual(mergedWithoutClone.a, firstObject.a) t.notEqual(mergedWithoutClone.b, secondObject.b) diff --git a/test/merge-plain-objects.js b/test/merge-plain-objects.ts similarity index 82% rename from test/merge-plain-objects.js rename to test/merge-plain-objects.ts index 29dcca3..91e59ba 100644 --- a/test/merge-plain-objects.js +++ b/test/merge-plain-objects.ts @@ -1,12 +1,12 @@ -const merge = require(`../`).default -const test = require(`tape`) +import deepmerge from "deepmerge" +import test from "tape" test(`plain objects are merged by default`, (t) => { const input = { newObject: new Object(), objectLiteral: { a: 123 }, } - const output = merge({}, input) + const output = deepmerge({}, input) t.deepEqual(output.newObject, input.newObject) t.notEqual(output.newObject, input.newObject) @@ -22,7 +22,7 @@ test(`instantiated objects are copied by reference`, (t) => { error: new Error(), regex: /regex/, } - const output = merge({}, input) + const output = deepmerge({}, input) t.equal(output.date, input.date) t.equal(output.error, input.error) diff --git a/test/merge.js b/test/merge.ts similarity index 78% rename from test/merge.js rename to test/merge.ts index 397937c..a29a11d 100644 --- a/test/merge.js +++ b/test/merge.ts @@ -1,11 +1,12 @@ -const merge = require(`../`).default -const test = require(`tape`) +import type { Options } from "deepmerge" +import deepmerge from "deepmerge" +import test from "tape" test(`add keys in target that do not exist at the root`, (t) => { const src = { key1: `value1`, key2: `value2` } const target = {} - const res = merge(target, src) + const res = deepmerge(target, src) t.deepEqual(target, {}, `merge should be immutable`) t.deepEqual(res, src) @@ -23,7 +24,7 @@ test(`merge existing simple keys in target at the roots`, (t) => { } t.deepEqual(target, { key1: `value1`, key3: `value3` }) - t.deepEqual(merge(target, src), expected) + t.deepEqual(deepmerge(target, src), expected) t.end() }) @@ -55,7 +56,7 @@ test(`merge nested objects into target`, (t) => { subkey2: `value2`, }, }) - t.deepEqual(merge(target, src), expected) + t.deepEqual(deepmerge(target, src), expected) t.end() }) @@ -80,7 +81,7 @@ test(`replace simple key with nested object in target`, (t) => { } t.deepEqual(target, { key1: `value1`, key2: `value2` }) - t.deepEqual(merge(target, src), expected) + t.deepEqual(deepmerge(target, src), expected) t.end() }) @@ -102,7 +103,7 @@ test(`should add nested object in target`, (t) => { }, } - t.deepEqual(merge(target, src), expected) + t.deepEqual(deepmerge(target, src), expected) t.end() }) @@ -128,7 +129,7 @@ test(`should clone source and target`, (t) => { }, } - const merged = merge(target, src, { clone: true }) + const merged = deepmerge(target, src, { clone: true }) t.deepEqual(merged, expected) @@ -151,7 +152,7 @@ test(`should clone source and target`, (t) => { }, } - const merged = merge(target, src) + const merged = deepmerge(target, src) t.notEqual(merged.a, target.a) t.notEqual(merged.b, src.b) @@ -177,7 +178,7 @@ test(`should replace object with simple key in target`, (t) => { }, key2: `value2`, }) - t.deepEqual(merge(target, src), expected) + t.deepEqual(deepmerge(target, src), expected) t.end() }) @@ -188,7 +189,7 @@ test(`should replace objects with arrays`, (t) => { const expected = { key1: [ `subkey` ] } - t.deepEqual(merge(target, src), expected) + t.deepEqual(deepmerge(target, src), expected) t.end() }) @@ -199,7 +200,7 @@ test(`should replace arrays with objects`, (t) => { const expected = { key1: { subkey: `one` } } - t.deepEqual(merge(target, src), expected) + t.deepEqual(deepmerge(target, src), expected) t.end() }) @@ -210,7 +211,7 @@ test(`should replace dates with arrays`, (t) => { const expected = { key1: [ `subkey` ] } - t.deepEqual(merge(target, src), expected) + t.deepEqual(deepmerge(target, src), expected) t.end() }) @@ -227,7 +228,7 @@ test(`should replace null with arrays`, (t) => { key1: [ `subkey` ], } - t.deepEqual(merge(target, src), expected) + t.deepEqual(deepmerge(target, src), expected) t.end() }) @@ -237,8 +238,8 @@ test(`should work on simple array`, (t) => { const expected = [ `one`, `two`, `one`, `three` ] - t.deepEqual(merge(target, src), expected) - t.ok(Array.isArray(merge(target, src))) + t.deepEqual(deepmerge(target, src), expected) + t.ok(Array.isArray(deepmerge(target, src))) t.end() }) @@ -248,8 +249,8 @@ test(`should work on another simple array`, (t) => { const 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.deepEqual(deepmerge(target, src), expected) + t.ok(Array.isArray(deepmerge(target, src))) t.end() }) @@ -267,9 +268,9 @@ test(`should work on array properties`, (t) => { 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.deepEqual(deepmerge(target, src), expected) + t.ok(Array.isArray(deepmerge(target, src).key1)) + t.ok(Array.isArray(deepmerge(target, src).key2)) t.end() }) @@ -285,7 +286,7 @@ test(`should work on array properties with clone option`, (t) => { t.deepEqual(target, { key1: [ `one`, `two` ], }) - const merged = merge(target, src, { clone: true }) + const merged = deepmerge(target, src, { clone: true }) t.notEqual(merged.key1, src.key1) t.notEqual(merged.key1, target.key1) t.notEqual(merged.key2, src.key2) @@ -309,9 +310,9 @@ test(`should work on array of objects`, (t) => { { 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.deepEqual(deepmerge(target, src), expected) + t.ok(Array.isArray(deepmerge(target, src)), `result should be an array`) + t.ok(Array.isArray(deepmerge(target, src)[0].key1), `subkey should be an array too`) t.end() }) @@ -333,13 +334,12 @@ test(`should work on array of objects with clone option`, (t) => { { key3: [ `five` ] }, ] - const merged = merge(target, src, { clone: true }) + const merged = deepmerge(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.ok(Array.isArray(deepmerge(target, src)), `result should be an array`) + t.ok(Array.isArray(deepmerge(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() @@ -350,8 +350,8 @@ test(`should treat regular expressions like primitive values`, (t) => { const src = { key1: /efg/ } const expected = { key1: /efg/ } - t.deepEqual(merge(target, src), expected) - t.deepEqual(merge(target, src).key1.test(`efg`), true) + t.deepEqual(deepmerge(target, src), expected) + t.deepEqual(deepmerge(target, src).key1.test(`efg`), true) t.end() }) @@ -360,7 +360,7 @@ test(`should treat regular expressions like primitive values and should not` const target = { key1: /abc/ } const src = { key1: /efg/ } - const output = merge(target, src, { clone: true }) + const output = deepmerge(target, src, { clone: true }) t.equal(output.key1, src.key1) t.end() @@ -381,7 +381,7 @@ test(`should treat dates like primitives`, (t) => { const expected = { key: tuesday, } - const actual = merge(target, source) + const actual = deepmerge(target, source) t.deepEqual(actual, expected) t.equal(actual.key.valueOf(), tuesday.valueOf()) @@ -400,29 +400,29 @@ test(`should treat dates like primitives and should not clone even with clone` key: tuesday, } - const actual = merge(target, source, { clone: true }) + const actual = deepmerge(target, source, { clone: true }) t.equal(actual.key, tuesday) t.end() }) test(`should work on array with null in it`, (t) => { - const target = [] + const target = [] as Array const src = [ null ] const expected = [ null ] - t.deepEqual(merge(target, src), expected) + t.deepEqual(deepmerge(target, src), expected) t.end() }) test(`should clone array's element if it is object`, (t) => { const a = { key: `yup` } - const target = [] + const target = [] as Array const source = [ a ] - const output = merge(target, source, { clone: true }) + const output = deepmerge(target, source, { clone: true }) t.notEqual(output[0], a) t.equal(output[0].key, `yup`) @@ -433,7 +433,7 @@ test(`should clone an array property when there is no target array`, (t) => { const someObject = {} const target = {} const source = { ary: [ someObject ] } - const output = merge(target, source, { clone: true }) + const output = deepmerge(target, source, { clone: true }) t.deepEqual(output, { ary: [{}] }) t.notEqual(output.ary[0], someObject) @@ -447,14 +447,14 @@ test(`should overwrite values when property is initialised but undefined`, (t) = const src = { value: undefined } - function hasUndefinedProperty(o) { + function hasUndefinedProperty(o: any) { t.ok(Object.prototype.hasOwnProperty.call(o, `value`)) t.equal(typeof o.value, `undefined`) } - hasUndefinedProperty(merge(target1, src)) - hasUndefinedProperty(merge(target2, src)) - hasUndefinedProperty(merge(target3, src)) + hasUndefinedProperty(deepmerge(target1, src)) + hasUndefinedProperty(deepmerge(target2, src)) + hasUndefinedProperty(deepmerge(target3, src)) t.end() }) @@ -467,7 +467,7 @@ test(`dates should copy correctly in an array`, (t) => { const source = [ tuesday, `lol` ] const expected = [ monday, `dude`, tuesday, `lol` ] - const actual = merge(target, source) + const actual = deepmerge(target, source) t.deepEqual(actual, expected) t.end() @@ -491,9 +491,9 @@ test(`should handle custom merge functions`, (t) => { }, } - const mergePeople = (target, source, options) => { + const mergePeople = (target: Record, source: Record) => { const keys = new Set(Object.keys(target).concat(Object.keys(source))) - const destination = {} + const destination: Record = {} keys.forEach((key) => { if (key in target && key in source) { destination[key] = `${ target[key] }-${ source[key] }` @@ -506,13 +506,13 @@ test(`should handle custom merge functions`, (t) => { return destination } - const options = { - customMerge: (key, options) => { + const options: Options = { + customMerge: (key) => { if (key === `people`) { return mergePeople } - return merge + return deepmerge }, } @@ -525,7 +525,7 @@ test(`should handle custom merge functions`, (t) => { }, } - const actual = merge(target, source, options) + const actual = deepmerge(target, source, options) t.deepEqual(actual, expected) t.end() }) @@ -549,11 +549,10 @@ test(`should handle custom merge functions`, (t) => { }, } - const mergeLetters = (target, source, options) => `merged letters` - + const mergeLetters = () => `merged letters` const options = { - customMerge: (key, options) => { + customMerge: (key: string) => { if (key === `letters`) { return mergeLetters } @@ -569,7 +568,7 @@ test(`should handle custom merge functions`, (t) => { }, } - const actual = merge(target, source, options) + const actual = deepmerge(target, source, options) t.deepEqual(actual, expected) t.end() }) @@ -592,8 +591,8 @@ test(`should merge correctly if custom merge is not a valid function`, (t) => { }, } - const options = { - customMerge: (key, options) => false, + const options: Options = { + customMerge: () => undefined, } const expected = { @@ -605,7 +604,7 @@ test(`should merge correctly if custom merge is not a valid function`, (t) => { }, } - const actual = merge(target, source, options) + const actual = deepmerge(target, source, options) t.deepEqual(actual, expected) t.end() }) @@ -615,7 +614,7 @@ test(`copy symbol keys in target that do not exist on the target`, (t) => { const src = { [mySymbol]: `value1` } const target = {} - const res = merge(target, src) + const res = deepmerge(target, src) t.equal(res[mySymbol], `value1`) t.deepEqual(Object.getOwnPropertySymbols(res), Object.getOwnPropertySymbols(src)) @@ -627,7 +626,7 @@ test(`copy symbol keys in target that do exist on the target`, (t) => { const src = { [mySymbol]: `value1` } const target = { [mySymbol]: `wat` } - const res = merge(target, src) + const res = deepmerge(target, src) t.equal(res[mySymbol], `value1`) t.end() @@ -636,7 +635,7 @@ test(`copy symbol keys in target that do exist on the target`, (t) => { test(`should not mutate options`, (t) => { const options = {} - merge({}, {}, options) + deepmerge({}, {}, options) t.deepEqual(options, {}) t.end() @@ -655,7 +654,7 @@ test(`Falsey properties should be mergeable`, (t) => { let customMergeWasCalled = false - const result = merge(target, source, { + const result = deepmerge(target, source, { isMergeable() { return true }, @@ -675,7 +674,7 @@ test(`Falsey properties should be mergeable`, (t) => { test(`should not mutate options`, (t) => { const options = {} - merge({}, {}, options) + deepmerge({}, {}, options) t.deepEqual(options, {}) t.end() diff --git a/test/prototype-poisoning.js b/test/prototype-poisoning.ts similarity index 72% rename from test/prototype-poisoning.js rename to test/prototype-poisoning.ts index 2a9d72d..444b940 100644 --- a/test/prototype-poisoning.js +++ b/test/prototype-poisoning.ts @@ -1,11 +1,12 @@ -const merge = require(`../`).default -const test = require(`tape`) -const isMergeableObject = require(`is-mergeable-object`) +import type { Options } from "deepmerge" +import deepmerge from "deepmerge" +import test from "tape" +import isPlainObj from 'is-plain-obj' test(`merging objects with own __proto__`, (t) => { const user = {} const malicious = JSON.parse(`{ "__proto__": { "admin": true } }`) - const mergedObject = merge(user, malicious) + const mergedObject = deepmerge(user, malicious) t.notOk(mergedObject.__proto__.admin, `non-plain properties should not be merged`) t.notOk(mergedObject.admin, `the destination should have an unmodified prototype`) t.end() @@ -28,7 +29,7 @@ test(`merging objects with plain and non-plain properties`, (t) => { [plainSymbolKey]: `qux`, } - const mergedObject = merge(target, source) + const mergedObject = deepmerge(target, source) t.equal(undefined, mergedObject.parentKey, `inherited properties of target should be removed, not merged or ignored`) t.equal(`bar`, mergedObject.plainKey, `enumerable own properties of target should be merged`) t.equal(`baz`, mergedObject.newKey, `properties not yet on target should be merged`) @@ -40,21 +41,20 @@ test(`merging objects with plain and non-plain properties`, (t) => { test(`merging strings works with a custom string merge`, (t) => { const target = { name: `Alexander` } const source = { name: `Hamilton` } - function customMerge(key, options) { + const customMerge: Options[`customMerge`] = (key) => { if (key === `name`) { - return function(target, source, options) { - return target[0] + `. ` + source.substring(0, 3) + return function(target: string, source: string, options) { + return `${ target[0] }. ${ source.substring(0, 3) }` } } else { - return merge + return deepmerge } } - function mergeable(target) { - return isMergeableObject(target) || (typeof target === `string` && target.length > 1) - } + const mergeable: Options[`isMergeable`] = (target) => + isPlainObj(target) || (typeof target === `string` && target.length > 1) - t.equal(`A. Ham`, merge(target, source, { customMerge, isMergeable: mergeable }).name) + t.equal(`A. Ham`, deepmerge(target, source, { customMerge, isMergeable: mergeable }).name) t.end() }) @@ -73,6 +73,6 @@ test(`merging objects with null prototype`, (t) => { }, } - t.deepEqual(expected, merge(target, source)) + t.deepEqual(expected, deepmerge(target, source)) t.end() }) diff --git a/test/types/test.ts b/test/types/test.ts index d104469..f020ae2 100644 --- a/test/types/test.ts +++ b/test/types/test.ts @@ -1,4 +1,4 @@ -import deepmerge, { deepmergeAll } from "../../src" +import deepmerge, { deepmergeAll } from "deepmerge" const a = { foo: `abc`, diff --git a/tsconfig.test.json b/tsconfig.test.json new file mode 100644 index 0000000..9cb74ce --- /dev/null +++ b/tsconfig.test.json @@ -0,0 +1,18 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "commonjs", + "removeComments": false, + "noFallthroughCasesInSwitch": false, + "noImplicitAny": false, + "noImplicitReturns": false, + "noImplicitThis": false, + "noUnusedLocals": false, + "noUnusedParameters": false, + "strict": false, + "strictBindCallApply": false, + "strictFunctionTypes": false, + "strictNullChecks": false, + "strictPropertyInitialization": false + } +}