diff --git a/CHANGELOG.md b/CHANGELOG.md index d17a262c..8cea8a80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,13 @@ # Changelog All notable changes to this project will be documented in this file. Dates are displayed in UTC. +# [5.0.0-next.2](https://github.com/RebeccaStevens/deepmerge-ts/compare/v5.0.0-next.1...v5.0.0-next.2) (2023-02-06) + + +### Features + +* create deepmergeInto function ([9c350a0](https://github.com/RebeccaStevens/deepmerge-ts/commit/9c350a051c16534907da459ff466a353b90d5505)), closes [#51](https://github.com/RebeccaStevens/deepmerge-ts/issues/51) + # [5.0.0-next.1](https://github.com/RebeccaStevens/deepmerge-ts/compare/v4.2.2...v5.0.0-next.1) (2023-02-02) diff --git a/dist/deno/README.md b/dist/deno/README.md index 0235e0ef..0100a4d6 100644 --- a/dist/deno/README.md +++ b/dist/deno/README.md @@ -114,9 +114,20 @@ console.log(merged); You can try out this example at [codesandbox.io](https://codesandbox.io/s/deepmerge-ts-example-iltxby?file=/src/example.ts). -### Using customized config +### Merging into a Target -[See deepmerge custom docs](./docs/deepmergeCustom.md). +You can use `deepmergeInto` if you want to update a target object with the merge result instead of creating a new object. + +This function is best used with objects that are all of the same type. + +Note: If the target object's type is different to the input objects, we'll assert that the target's type has changed (this is not done automatically with `deepmergeIntoCustom`). + +### Customized the Merging Process + +We provide a customizer function for each of our main deepmerge functions: `deepmergeCustom` and `deepmergeIntoCustom`. +You can use these to customize the details of how values should be merged together. + +See [deepmerge custom docs](./docs/deepmergeCustom.md) for more details. ## Performance @@ -140,4 +151,4 @@ In addition to performance improvements, this strategy merges multiple inputs at ## API -[See API docs](./docs/API.md). +See [API docs](./docs/API.md). diff --git a/dist/deno/actions.ts b/dist/deno/actions.ts new file mode 100644 index 00000000..e597db1f --- /dev/null +++ b/dist/deno/actions.ts @@ -0,0 +1,14 @@ +/** + * Special values that tell deepmerge to perform a certain action. + */ +export const actions = { + defaultMerge: Symbol("deepmerge-ts: default merge"), + skip: Symbol("deepmerge-ts: skip"), +} as const; + +/** + * Special values that tell deepmergeInto to perform a certain action. + */ +export const actionsInto = { + defaultMerge: actions.defaultMerge, +} as const; diff --git a/dist/deno/deepmerge-into.ts b/dist/deno/deepmerge-into.ts new file mode 100644 index 00000000..0ad707d2 --- /dev/null +++ b/dist/deno/deepmerge-into.ts @@ -0,0 +1,394 @@ +import { actionsInto as actions } from "./actions.ts"; +import * as defaultMergeIntoFunctions from "./defaults/into.ts"; +import { defaultMetaDataUpdater } from "./defaults/meta-data-updater.ts"; +import type { + DeepMergeBuiltInMetaData, + DeepMergeIntoOptions, + DeepMergeMergeIntoFunctionUtils, + Reference, + DeepMergeHKT, + DeepMergeMergeFunctionsDefaultURIs, +} from "./types/index.ts"; +import type { FlatternAlias } from "./types/utils.ts"; +import { getObjectType, ObjectType } from "./utils.ts"; + +/** + * Deeply merge objects into a target. + * + * @param target - This object will be mutated with the merge result. + * @param objects - The objects to merge into the target. + */ +export function deepmergeInto( + target: T, + ...objects: ReadonlyArray +): void; + +/** + * Deeply merge objects into a target. + * + * @param target - This object will be mutated with the merge result. + * @param objects - The objects to merge into the target. + */ +export function deepmergeInto< + Target extends object, + Ts extends ReadonlyArray +>( + target: Target, + ...objects: Ts +): asserts target is FlatternAlias< + Target & + DeepMergeHKT< + [Target, ...Ts], + DeepMergeMergeFunctionsDefaultURIs, + DeepMergeBuiltInMetaData + > +>; + +export function deepmergeInto< + Target extends object, + Ts extends ReadonlyArray +>( + target: Target, + ...objects: Ts +): asserts target is FlatternAlias< + Target & + DeepMergeHKT< + [Target, ...Ts], + DeepMergeMergeFunctionsDefaultURIs, + DeepMergeBuiltInMetaData + > +> { + return void deepmergeIntoCustom({})(target, ...objects); +} + +/** + * Deeply merge two or more objects using the given options. + * + * @param options - The options on how to customize the merge function. + */ +export function deepmergeIntoCustom( + options: DeepMergeIntoOptions< + DeepMergeBuiltInMetaData, + DeepMergeBuiltInMetaData + > +): >( + target: Target, + ...objects: Ts +) => void; + +/** + * Deeply merge two or more objects using the given options and meta data. + * + * @param options - The options on how to customize the merge function. + * @param rootMetaData - The meta data passed to the root items' being merged. + */ +export function deepmergeIntoCustom< + MetaData, + MetaMetaData extends DeepMergeBuiltInMetaData = DeepMergeBuiltInMetaData +>( + options: DeepMergeIntoOptions, + rootMetaData?: MetaData +): >( + target: Target, + ...objects: Ts +) => void; + +export function deepmergeIntoCustom< + MetaData, + MetaMetaData extends DeepMergeBuiltInMetaData +>( + options: DeepMergeIntoOptions, + rootMetaData?: MetaData +): >( + target: Target, + ...objects: Ts +) => void { + /** + * The type of the customized deepmerge function. + */ + type CustomizedDeepmergeInto = < + Target extends object, + Ts extends ReadonlyArray + >( + target: Target, + ...objects: Ts + ) => void; + + const utils: DeepMergeMergeIntoFunctionUtils = + getIntoUtils(options, customizedDeepmergeInto as CustomizedDeepmergeInto); + + /** + * The customized deepmerge function. + */ + function customizedDeepmergeInto( + target: object, + ...objects: ReadonlyArray + ) { + mergeUnknownsInto< + ReadonlyArray, + typeof utils, + MetaData, + MetaMetaData + >({ value: target }, [target, ...objects], utils, rootMetaData); + } + + return customizedDeepmergeInto as CustomizedDeepmergeInto; +} + +/** + * The the utils that are available to the merge functions. + * + * @param options - The options the user specified + */ +function getIntoUtils( + options: DeepMergeIntoOptions, + customizedDeepmergeInto: DeepMergeMergeIntoFunctionUtils< + M, + MM + >["deepmergeInto"] +): DeepMergeMergeIntoFunctionUtils { + return { + defaultMergeFunctions: defaultMergeIntoFunctions, + mergeFunctions: { + ...defaultMergeIntoFunctions, + ...Object.fromEntries( + Object.entries(options) + .filter(([key, option]) => + Object.prototype.hasOwnProperty.call(defaultMergeIntoFunctions, key) + ) + .map(([key, option]) => + option === false + ? [key, defaultMergeIntoFunctions.mergeOthers] + : [key, option] + ) + ), + } as DeepMergeMergeIntoFunctionUtils["mergeFunctions"], + metaDataUpdater: (options.metaDataUpdater ?? + defaultMetaDataUpdater) as unknown as DeepMergeMergeIntoFunctionUtils< + M, + MM + >["metaDataUpdater"], + deepmergeInto: customizedDeepmergeInto, + actions, + }; +} + +/** + * Merge unknown things into a target. + * + * @param m_target - The target to merge into. + * @param values - The values. + */ +export function mergeUnknownsInto< + Ts extends ReadonlyArray, + U extends DeepMergeMergeIntoFunctionUtils, + M, + MM extends DeepMergeBuiltInMetaData +>( + m_target: Reference, + values: Ts, + utils: U, + meta: M | undefined + // eslint-disable-next-line @typescript-eslint/no-invalid-void-type +): void | symbol { + if (values.length === 0) { + return; + } + if (values.length === 1) { + return void mergeOthersInto(m_target, values, utils, meta); + } + + const type = getObjectType(m_target.value); + + // eslint-disable-next-line functional/no-conditional-statements -- add an early escape for better performance. + if (type !== ObjectType.NOT && type !== ObjectType.OTHER) { + // eslint-disable-next-line functional/no-loop-statements -- using a loop here is more performant than mapping every value and then testing every value. + for (let m_index = 1; m_index < values.length; m_index++) { + if (getObjectType(values[m_index]) === type) { + continue; + } + + return void mergeOthersInto(m_target, values, utils, meta); + } + } + + switch (type) { + case ObjectType.RECORD: { + return void mergeRecordsInto( + m_target as Reference>, + values as ReadonlyArray>>, + utils, + meta + ); + } + + case ObjectType.ARRAY: { + return void mergeArraysInto( + m_target as Reference, + values as ReadonlyArray>, + utils, + meta + ); + } + + case ObjectType.SET: { + return void mergeSetsInto( + m_target as Reference>, + values as ReadonlyArray>>, + utils, + meta + ); + } + + case ObjectType.MAP: { + return void mergeMapsInto( + m_target as Reference>, + values as ReadonlyArray>>, + utils, + meta + ); + } + + default: { + return void mergeOthersInto(m_target, values, utils, meta); + } + } +} + +/** + * Merge records into a target record. + * + * @param m_target - The target to merge into. + * @param values - The records. + */ +function mergeRecordsInto< + U extends DeepMergeMergeIntoFunctionUtils, + M, + MM extends DeepMergeBuiltInMetaData +>( + m_target: Reference>, + values: ReadonlyArray>>, + utils: U, + meta: M | undefined +) { + const action = utils.mergeFunctions.mergeRecords( + m_target, + values, + utils, + meta + ); + + if (action === actions.defaultMerge) { + utils.defaultMergeFunctions.mergeRecords< + ReadonlyArray>>, + U, + M, + MM + >(m_target, values, utils, meta); + } +} + +/** + * Merge arrays into a target array. + * + * @param m_target - The target to merge into. + * @param values - The arrays. + */ +function mergeArraysInto< + U extends DeepMergeMergeIntoFunctionUtils, + M, + MM extends DeepMergeBuiltInMetaData +>( + m_target: Reference, + values: ReadonlyArray>, + utils: U, + meta: M | undefined +) { + const action = utils.mergeFunctions.mergeArrays( + m_target, + values, + utils, + meta + ); + + if (action === actions.defaultMerge) { + utils.defaultMergeFunctions.mergeArrays(m_target, values); + } +} + +/** + * Merge sets into a target set. + * + * @param m_target - The target to merge into. + * @param values - The sets. + */ +function mergeSetsInto< + U extends DeepMergeMergeIntoFunctionUtils, + M, + MM extends DeepMergeBuiltInMetaData +>( + m_target: Reference>, + values: ReadonlyArray>>, + utils: U, + meta: M | undefined +) { + const action = utils.mergeFunctions.mergeSets(m_target, values, utils, meta); + + if (action === actions.defaultMerge) { + utils.defaultMergeFunctions.mergeSets(m_target, values); + } +} + +/** + * Merge maps into a target map. + * + * @param m_target - The target to merge into. + * @param values - The maps. + */ +function mergeMapsInto< + U extends DeepMergeMergeIntoFunctionUtils, + M, + MM extends DeepMergeBuiltInMetaData +>( + m_target: Reference>, + values: ReadonlyArray>>, + utils: U, + meta: M | undefined +) { + const action = utils.mergeFunctions.mergeMaps(m_target, values, utils, meta); + + if (action === actions.defaultMerge) { + utils.defaultMergeFunctions.mergeMaps(m_target, values); + } +} + +/** + * Merge other things into a target. + * + * @param m_target - The target to merge into. + * @param values - The other things. + */ +function mergeOthersInto< + U extends DeepMergeMergeIntoFunctionUtils, + M, + MM extends DeepMergeBuiltInMetaData +>( + m_target: Reference, + values: ReadonlyArray, + utils: U, + meta: M | undefined +) { + const action = utils.mergeFunctions.mergeOthers( + m_target, + values, + utils, + meta + ); + + if ( + action === actions.defaultMerge || + m_target.value === actions.defaultMerge + ) { + utils.defaultMergeFunctions.mergeOthers(m_target, values); + } +} diff --git a/dist/deno/deepmerge.ts b/dist/deno/deepmerge.ts index ab3ddfe0..87dce761 100644 --- a/dist/deno/deepmerge.ts +++ b/dist/deno/deepmerge.ts @@ -1,54 +1,16 @@ +import { actions } from "./actions.ts"; +import { defaultMetaDataUpdater } from "./defaults/meta-data-updater.ts"; +import * as defaultMergeFunctions from "./defaults/vanilla.ts"; import type { DeepMergeBuiltInMetaData, DeepMergeHKT, - DeepMergeArraysDefaultHKT, DeepMergeMergeFunctionsDefaultURIs, - DeepMergeMapsDefaultHKT, DeepMergeMergeFunctionsURIs, DeepMergeOptions, - DeepMergeRecordsDefaultHKT, - DeepMergeSetsDefaultHKT, DeepMergeMergeFunctionUtils, GetDeepMergeMergeFunctionsURIs, } from "./types/index.ts"; -import { - getIterableOfIterables, - getKeys, - getObjectType, - ObjectType, - objectHasProperty, -} from "./utils.ts"; - -const defaultMergeFunctions = { - mergeMaps: defaultMergeMaps, - mergeSets: defaultMergeSets, - mergeArrays: defaultMergeArrays, - mergeRecords: defaultMergeRecords, - mergeOthers: leaf, -} as const; - -/** - * Special values that tell deepmerge-ts to perform a certain action. - */ -const actions = { - defaultMerge: Symbol("deepmerge-ts: default merge"), - skip: Symbol("deepmerge-ts: skip"), -} as const; - -/** - * The default function to update meta data. - */ -function defaultMetaDataUpdater( - previousMeta: M, - metaMeta: DeepMergeBuiltInMetaData -): DeepMergeBuiltInMetaData { - return metaMeta; -} - -/** - * The default merge functions. - */ -export type DeepMergeMergeFunctionsDefaults = typeof defaultMergeFunctions; +import { getObjectType, ObjectType } from "./utils.ts"; /** * Deeply merge objects. @@ -142,7 +104,7 @@ export function deepmergeCustom< } /** - * The the full options with defaults apply. + * The the utils that are available to the merge functions. * * @param options - The options the user specified */ @@ -160,7 +122,9 @@ function getUtils( Object.prototype.hasOwnProperty.call(defaultMergeFunctions, key) ) .map(([key, option]) => - option === false ? [key, leaf] : [key, option] + option === false + ? [key, defaultMergeFunctions.mergeOthers] + : [key, option] ) ), } as DeepMergeMergeFunctionUtils["mergeFunctions"], @@ -180,7 +144,7 @@ function getUtils( * * @param values - The values. */ -function mergeUnknowns< +export function mergeUnknowns< Ts extends ReadonlyArray, U extends DeepMergeMergeFunctionUtils, MF extends DeepMergeMergeFunctionsURIs, @@ -400,110 +364,3 @@ function mergeOthers< } return result; } - -/** - * The default strategy to merge records. - * - * @param values - The records. - */ -function defaultMergeRecords< - Ts extends ReadonlyArray>, - U extends DeepMergeMergeFunctionUtils, - MF extends DeepMergeMergeFunctionsURIs, - M, - MM extends DeepMergeBuiltInMetaData ->( - values: Ts, - utils: U, - meta: M | undefined -): DeepMergeRecordsDefaultHKT { - const result: Record = {}; - - /* eslint-disable functional/no-loop-statements, functional/no-conditional-statements -- using a loop here is more performant. */ - - for (const key of getKeys(values)) { - const propValues = []; - - for (const value of values) { - if (objectHasProperty(value, key)) { - propValues.push(value[key]); - } - } - - if (propValues.length === 0) { - continue; - } - - const updatedMeta = utils.metaDataUpdater(meta, { - key, - parents: values, - } as unknown as MM); - - const propertyResult = mergeUnknowns, U, MF, M, MM>( - propValues, - utils, - updatedMeta - ); - - if (propertyResult === actions.skip) { - continue; - } - - if (key === "__proto__") { - Object.defineProperty(result, key, { - value: propertyResult, - configurable: true, - enumerable: true, - writable: true, - }); - } else { - result[key] = propertyResult; - } - } - - /* eslint-enable functional/no-loop-statements, functional/no-conditional-statements */ - - return result as DeepMergeRecordsDefaultHKT; -} - -/** - * The default strategy to merge arrays. - * - * @param values - The arrays. - */ -function defaultMergeArrays< - Ts extends ReadonlyArray>, - MF extends DeepMergeMergeFunctionsURIs, - M ->(values: Ts): DeepMergeArraysDefaultHKT { - return values.flat() as DeepMergeArraysDefaultHKT; -} - -/** - * The default strategy to merge sets. - * - * @param values - The sets. - */ -function defaultMergeSets< - Ts extends ReadonlyArray>> ->(values: Ts): DeepMergeSetsDefaultHKT { - return new Set(getIterableOfIterables(values)) as DeepMergeSetsDefaultHKT; -} - -/** - * The default strategy to merge maps. - * - * @param values - The maps. - */ -function defaultMergeMaps< - Ts extends ReadonlyArray>> ->(values: Ts): DeepMergeMapsDefaultHKT { - return new Map(getIterableOfIterables(values)) as DeepMergeMapsDefaultHKT; -} - -/** - * Get the last value in the given array. - */ -function leaf>(values: Ts) { - return values[values.length - 1]; -} diff --git a/dist/deno/defaults/into.ts b/dist/deno/defaults/into.ts new file mode 100644 index 00000000..2a07f0d5 --- /dev/null +++ b/dist/deno/defaults/into.ts @@ -0,0 +1,133 @@ +import { mergeUnknownsInto } from "../deepmerge-into.ts"; +import type { + DeepMergeBuiltInMetaData, + DeepMergeMergeIntoFunctionUtils, + Reference, +} from "../types/index.ts"; +import { + getIterableOfIterables, + getKeys, + objectHasProperty, +} from "../utils.ts"; + +/** + * The default merge functions. + */ +export type MergeFunctions = { + mergeRecords: typeof mergeRecords; + mergeArrays: typeof mergeArrays; + mergeSets: typeof mergeSets; + mergeMaps: typeof mergeMaps; + mergeOthers: typeof mergeOthers; +}; + +/** + * The default strategy to merge records into a target record. + * + * @param m_target - The result will be mutated into this record + * @param values - The records (including the target's value if there is one). + */ +export function mergeRecords< + Ts extends ReadonlyArray>, + U extends DeepMergeMergeIntoFunctionUtils, + M, + MM extends DeepMergeBuiltInMetaData +>( + m_target: Reference>, + values: Ts, + utils: U, + meta: M | undefined +): void { + /* eslint-disable functional/no-loop-statements, functional/no-conditional-statements -- using a loop here is more performant. */ + + for (const key of getKeys(values)) { + const propValues = []; + + for (const value of values) { + if (objectHasProperty(value, key)) { + propValues.push(value[key]); + } + } + + if (propValues.length === 0) { + continue; + } + + const updatedMeta = utils.metaDataUpdater(meta, { + key, + parents: values, + } as unknown as MM); + + const propertyTarget: Reference = { value: propValues[0] }; + mergeUnknownsInto, U, M, MM>( + propertyTarget, + propValues, + utils, + updatedMeta + ); + + if (key === "__proto__") { + Object.defineProperty(m_target, key, { + value: propertyTarget.value, + configurable: true, + enumerable: true, + writable: true, + }); + } else { + m_target.value[key] = propertyTarget.value; + } + } + + /* eslint-enable functional/no-loop-statements, functional/no-conditional-statements */ +} + +/** + * The default strategy to merge arrays into a target array. + * + * @param m_target - The result will be mutated into this array + * @param values - The arrays (including the target's value if there is one). + */ +export function mergeArrays>>( + m_target: Reference, + values: Ts +): void { + m_target.value.push(...values.slice(1).flat()); +} + +/** + * The default strategy to merge sets into a target set. + * + * @param m_target - The result will be mutated into this set + * @param values - The sets (including the target's value if there is one). + */ +export function mergeSets< + Ts extends ReadonlyArray>> +>(m_target: Reference>, values: Ts): void { + for (const value of getIterableOfIterables(values.slice(1))) { + m_target.value.add(value); + } +} + +/** + * The default strategy to merge maps into a target map. + * + * @param m_target - The result will be mutated into this map + * @param values - The maps (including the target's value if there is one). + */ +export function mergeMaps< + Ts extends ReadonlyArray>> +>(m_target: Reference>, values: Ts): void { + for (const [key, value] of getIterableOfIterables(values.slice(1))) { + m_target.value.set(key, value); + } +} + +/** + * Set the target to the last value. + */ +export function mergeOthers>( + m_target: Reference, + values: Ts +) { + m_target.value = values[values.length - 1]; +} diff --git a/dist/deno/defaults/meta-data-updater.ts b/dist/deno/defaults/meta-data-updater.ts new file mode 100644 index 00000000..d2994672 --- /dev/null +++ b/dist/deno/defaults/meta-data-updater.ts @@ -0,0 +1,11 @@ +import type { DeepMergeBuiltInMetaData } from "../types/index.ts"; + +/** + * The default function to update meta data. + */ +export function defaultMetaDataUpdater( + previousMeta: M, + metaMeta: DeepMergeBuiltInMetaData +): DeepMergeBuiltInMetaData { + return metaMeta; +} diff --git a/dist/deno/defaults/vanilla.ts b/dist/deno/defaults/vanilla.ts new file mode 100644 index 00000000..9dfa68fb --- /dev/null +++ b/dist/deno/defaults/vanilla.ts @@ -0,0 +1,134 @@ +import { actions } from "../actions.ts"; +import { mergeUnknowns } from "../deepmerge.ts"; +import type { + DeepMergeArraysDefaultHKT, + DeepMergeBuiltInMetaData, + DeepMergeMapsDefaultHKT, + DeepMergeMergeFunctionsURIs, + DeepMergeMergeFunctionUtils, + DeepMergeRecordsDefaultHKT, + DeepMergeSetsDefaultHKT, +} from "../types/index.ts"; +import { + getKeys, + objectHasProperty, + getIterableOfIterables, +} from "../utils.ts"; + +/** + * The default merge functions. + */ +export type MergeFunctions = { + mergeRecords: typeof mergeRecords; + mergeArrays: typeof mergeArrays; + mergeSets: typeof mergeSets; + mergeMaps: typeof mergeMaps; + mergeOthers: typeof mergeOthers; +}; + +/** + * The default strategy to merge records. + * + * @param values - The records. + */ +export function mergeRecords< + Ts extends ReadonlyArray>, + U extends DeepMergeMergeFunctionUtils, + MF extends DeepMergeMergeFunctionsURIs, + M, + MM extends DeepMergeBuiltInMetaData +>( + values: Ts, + utils: U, + meta: M | undefined +): DeepMergeRecordsDefaultHKT { + const result: Record = {}; + + /* eslint-disable functional/no-loop-statements, functional/no-conditional-statements -- using a loop here is more performant. */ + + for (const key of getKeys(values)) { + const propValues = []; + + for (const value of values) { + if (objectHasProperty(value, key)) { + propValues.push(value[key]); + } + } + + if (propValues.length === 0) { + continue; + } + + const updatedMeta = utils.metaDataUpdater(meta, { + key, + parents: values, + } as unknown as MM); + + const propertyResult = mergeUnknowns, U, MF, M, MM>( + propValues, + utils, + updatedMeta + ); + + if (propertyResult === actions.skip) { + continue; + } + + if (key === "__proto__") { + Object.defineProperty(result, key, { + value: propertyResult, + configurable: true, + enumerable: true, + writable: true, + }); + } else { + result[key] = propertyResult; + } + } + + /* eslint-enable functional/no-loop-statements, functional/no-conditional-statements */ + + return result as DeepMergeRecordsDefaultHKT; +} + +/** + * The default strategy to merge arrays. + * + * @param values - The arrays. + */ +export function mergeArrays< + Ts extends ReadonlyArray>, + MF extends DeepMergeMergeFunctionsURIs, + M +>(values: Ts): DeepMergeArraysDefaultHKT { + return values.flat() as DeepMergeArraysDefaultHKT; +} + +/** + * The default strategy to merge sets. + * + * @param values - The sets. + */ +export function mergeSets< + Ts extends ReadonlyArray>> +>(values: Ts): DeepMergeSetsDefaultHKT { + return new Set(getIterableOfIterables(values)) as DeepMergeSetsDefaultHKT; +} + +/** + * The default strategy to merge maps. + * + * @param values - The maps. + */ +export function mergeMaps< + Ts extends ReadonlyArray>> +>(values: Ts): DeepMergeMapsDefaultHKT { + return new Map(getIterableOfIterables(values)) as DeepMergeMapsDefaultHKT; +} + +/** + * Get the last value in the given array. + */ +export function mergeOthers>(values: Ts) { + return values[values.length - 1]; +} diff --git a/dist/deno/index.ts b/dist/deno/index.ts index 10c7f11f..9a3c701a 100644 --- a/dist/deno/index.ts +++ b/dist/deno/index.ts @@ -1,6 +1,8 @@ export { deepmerge, deepmergeCustom } from "./deepmerge.ts"; +export { deepmergeInto, deepmergeIntoCustom } from "./deepmerge-into.ts"; -export type { DeepMergeMergeFunctionsDefaults } from "./deepmerge.ts"; +export type { MergeFunctions as DeepMergeMergeIntoFunctionsDefaults } from "./defaults/into.ts"; +export type { MergeFunctions as DeepMergeMergeFunctionsDefaults } from "./defaults/vanilla.ts"; export type { DeepMergeArraysDefaultHKT, DeepMergeBuiltInMetaData, @@ -13,7 +15,10 @@ export type { DeepMergeMergeFunctionsURIs, DeepMergeMergeFunctionURItoKind, DeepMergeMergeFunctionUtils, + DeepMergeMergeIntoFunctionUtils, DeepMergeOptions, + DeepMergeIntoOptions, DeepMergeRecordsDefaultHKT, DeepMergeSetsDefaultHKT, + Reference as DeepMergeValueReference, } from "./types/index.ts"; diff --git a/dist/deno/types/options.ts b/dist/deno/types/options.ts index f03dd6ce..58be1983 100644 --- a/dist/deno/types/options.ts +++ b/dist/deno/types/options.ts @@ -1,6 +1,7 @@ -// eslint-disable-next-line import/no-relative-parent-imports -- use "deepmerge-ts" once denoify can support it. -import type { DeepMergeMergeFunctionsDefaults } from "../index.ts"; +import type { MergeFunctions as MergeIntoFunctions } from "../defaults/into.ts"; +import type { MergeFunctions } from "../defaults/vanilla.ts"; +// eslint-disable-next-line import/no-relative-parent-imports -- use "deepmerge-ts" once denoify can support it. import type { DeepMergeBuiltInMetaData } from "./merging.ts"; /** @@ -11,6 +12,14 @@ export type DeepMergeOptions< MM extends Readonly> = DeepMergeBuiltInMetaData > = Partial>; +/** + * The options the user can pass to customize deepmergeInto. + */ +export type DeepMergeIntoOptions< + in out M, + MM extends Readonly> = DeepMergeBuiltInMetaData +> = Partial>; + type MetaDataUpdater = ( previousMeta: M | undefined, metaMeta: Readonly> @@ -32,6 +41,28 @@ type DeepMergeOptionsFull< enableImplicitDefaultMerging: boolean; }>; +/** + * All the options the user can pass to customize deepmergeInto. + */ +type DeepMergeIntoOptionsFull< + in out M, + MM extends DeepMergeBuiltInMetaData +> = Readonly<{ + mergeRecords: DeepMergeMergeIntoFunctions["mergeRecords"] | false; + mergeArrays: DeepMergeMergeIntoFunctions["mergeArrays"] | false; + mergeMaps: DeepMergeMergeIntoFunctions["mergeMaps"] | false; + mergeSets: DeepMergeMergeIntoFunctions["mergeSets"] | false; + mergeOthers: DeepMergeMergeIntoFunctions["mergeOthers"]; + metaDataUpdater: MetaDataUpdater; +}>; + +/** + * An object that has a reference to a value. + */ +export type Reference = { + value: T; +}; + /** * All the merge functions that deepmerge uses. */ @@ -85,15 +116,77 @@ type DeepMergeMergeFunctions< ) => unknown; }>; +// eslint-disable-next-line @typescript-eslint/no-invalid-void-type +type DeepMergeMergeIntoFunctionsReturnType = void | symbol; + +/** + * All the merge functions that deepmerge uses. + */ +type DeepMergeMergeIntoFunctions< + in M, + MM extends DeepMergeBuiltInMetaData +> = Readonly<{ + mergeRecords: < + Ts extends ReadonlyArray>>, + U extends DeepMergeMergeIntoFunctionUtils + >( + m_target: Reference>, + values: Ts, + utils: U, + meta: M | undefined + ) => DeepMergeMergeIntoFunctionsReturnType; + + mergeArrays: < + Ts extends ReadonlyArray>, + U extends DeepMergeMergeIntoFunctionUtils + >( + m_target: Reference, + values: Ts, + utils: U, + meta: M | undefined + ) => DeepMergeMergeIntoFunctionsReturnType; + + mergeMaps: < + Ts extends ReadonlyArray>>, + U extends DeepMergeMergeIntoFunctionUtils + >( + m_target: Reference>, + values: Ts, + utils: U, + meta: M | undefined + ) => DeepMergeMergeIntoFunctionsReturnType; + + mergeSets: < + Ts extends ReadonlyArray>>, + U extends DeepMergeMergeIntoFunctionUtils + >( + m_target: Reference>, + values: Ts, + utils: U, + meta: M | undefined + ) => DeepMergeMergeIntoFunctionsReturnType; + + mergeOthers: < + Ts extends ReadonlyArray, + U extends DeepMergeMergeIntoFunctionUtils + >( + m_target: Reference, + values: Ts, + utils: U, + meta: M | undefined + ) => DeepMergeMergeIntoFunctionsReturnType; +}>; + /** * The utils provided to the merge functions. */ +// eslint-disable-next-line functional/no-mixed-types export type DeepMergeMergeFunctionUtils< in out M, MM extends DeepMergeBuiltInMetaData > = Readonly<{ mergeFunctions: DeepMergeMergeFunctions; - defaultMergeFunctions: DeepMergeMergeFunctionsDefaults; + defaultMergeFunctions: MergeFunctions; metaDataUpdater: MetaDataUpdater; deepmerge: >(...values: Ts) => unknown; useImplicitDefaultMerging: boolean; @@ -102,3 +195,23 @@ export type DeepMergeMergeFunctionUtils< skip: symbol; }>; }>; + +/** + * The utils provided to the merge functions. + */ +// eslint-disable-next-line functional/no-mixed-types +export type DeepMergeMergeIntoFunctionUtils< + in out M, + MM extends DeepMergeBuiltInMetaData +> = Readonly<{ + mergeFunctions: DeepMergeMergeIntoFunctions; + defaultMergeFunctions: MergeIntoFunctions; + metaDataUpdater: MetaDataUpdater; + deepmergeInto: >( + target: Target, + ...values: Ts + ) => void; + actions: Readonly<{ + defaultMerge: symbol; + }>; +}>; diff --git a/dist/deno/types/utils.ts b/dist/deno/types/utils.ts index fdc378fa..61fcf2ca 100644 --- a/dist/deno/types/utils.ts +++ b/dist/deno/types/utils.ts @@ -2,7 +2,9 @@ * Flatten a complex type such as a union or intersection of objects into a * single object. */ -export type FlatternAlias = { [P in keyof T]: T[P] } & {}; +export type FlatternAlias = Is extends true + ? T + : { [P in keyof T]: T[P] } & {}; /** * Get the value of the given key in the given object. diff --git a/dist/node/index.cjs b/dist/node/index.cjs index 2c966b51..455a21e9 100644 --- a/dist/node/index.cjs +++ b/dist/node/index.cjs @@ -1,6 +1,25 @@ 'use strict'; -Object.defineProperty(exports, '__esModule', { value: true }); +/** + * Special values that tell deepmerge to perform a certain action. + */ +const actions = { + defaultMerge: Symbol("deepmerge-ts: default merge"), + skip: Symbol("deepmerge-ts: skip"), +}; +/** + * Special values that tell deepmergeInto to perform a certain action. + */ +const actionsInto = { + defaultMerge: actions.defaultMerge, +}; + +/** + * The default function to update meta data. + */ +function defaultMetaDataUpdater(previousMeta, metaMeta) { + return metaMeta; +} /** * Get the type of the given object. @@ -110,26 +129,87 @@ function isRecord(value) { return true; } -const defaultMergeFunctions = { - mergeMaps: defaultMergeMaps, - mergeSets: defaultMergeSets, - mergeArrays: defaultMergeArrays, - mergeRecords: defaultMergeRecords, - mergeOthers: leaf, -}; /** - * Special values that tell deepmerge-ts to perform a certain action. + * The default strategy to merge records. + * + * @param values - The records. */ -const actions = { - defaultMerge: Symbol("deepmerge-ts: default merge"), - skip: Symbol("deepmerge-ts: skip"), -}; +function mergeRecords$2(values, utils, meta) { + const result = {}; + /* eslint-disable functional/no-loop-statements, functional/no-conditional-statements -- using a loop here is more performant. */ + for (const key of getKeys(values)) { + const propValues = []; + for (const value of values) { + if (objectHasProperty(value, key)) { + propValues.push(value[key]); + } + } + if (propValues.length === 0) { + continue; + } + const updatedMeta = utils.metaDataUpdater(meta, { + key, + parents: values, + }); + const propertyResult = mergeUnknowns(propValues, utils, updatedMeta); + if (propertyResult === actions.skip) { + continue; + } + if (key === "__proto__") { + Object.defineProperty(result, key, { + value: propertyResult, + configurable: true, + enumerable: true, + writable: true, + }); + } + else { + result[key] = propertyResult; + } + } + /* eslint-enable functional/no-loop-statements, functional/no-conditional-statements */ + return result; +} /** - * The default function to update meta data. + * The default strategy to merge arrays. + * + * @param values - The arrays. */ -function defaultMetaDataUpdater(previousMeta, metaMeta) { - return metaMeta; +function mergeArrays$2(values) { + return values.flat(); +} +/** + * The default strategy to merge sets. + * + * @param values - The sets. + */ +function mergeSets$2(values) { + return new Set(getIterableOfIterables(values)); +} +/** + * The default strategy to merge maps. + * + * @param values - The maps. + */ +function mergeMaps$2(values) { + return new Map(getIterableOfIterables(values)); } +/** + * Get the last value in the given array. + */ +function mergeOthers$2(values) { + return values[values.length - 1]; +} + +var defaultMergeFunctions = /*#__PURE__*/Object.freeze({ + __proto__: null, + mergeArrays: mergeArrays$2, + mergeMaps: mergeMaps$2, + mergeOthers: mergeOthers$2, + mergeRecords: mergeRecords$2, + mergeSets: mergeSets$2 +}); + /** * Deeply merge objects. * @@ -149,7 +229,7 @@ function deepmergeCustom(options, rootMetaData) { return customizedDeepmerge; } /** - * The the full options with defaults apply. + * The the utils that are available to the merge functions. * * @param options - The options the user specified */ @@ -161,7 +241,9 @@ function getUtils(options, customizedDeepmerge) { ...defaultMergeFunctions, ...Object.fromEntries(Object.entries(options) .filter(([key, option]) => Object.prototype.hasOwnProperty.call(defaultMergeFunctions, key)) - .map(([key, option]) => option === false ? [key, leaf] : [key, option])), + .map(([key, option]) => option === false + ? [key, mergeOthers$2] + : [key, option])), }, metaDataUpdater: ((_a = options.metaDataUpdater) !== null && _a !== void 0 ? _a : defaultMetaDataUpdater), deepmerge: customizedDeepmerge, @@ -179,7 +261,7 @@ function mergeUnknowns(values, utils, meta) { return undefined; } if (values.length === 1) { - return mergeOthers(values, utils, meta); + return mergeOthers$1(values, utils, meta); } const type = getObjectType(values[0]); // eslint-disable-next-line functional/no-conditional-statements -- add an early escape for better performance. @@ -189,24 +271,24 @@ function mergeUnknowns(values, utils, meta) { if (getObjectType(values[m_index]) === type) { continue; } - return mergeOthers(values, utils, meta); + return mergeOthers$1(values, utils, meta); } } switch (type) { case 1 /* ObjectType.RECORD */: { - return mergeRecords(values, utils, meta); + return mergeRecords$1(values, utils, meta); } case 2 /* ObjectType.ARRAY */: { - return mergeArrays(values, utils, meta); + return mergeArrays$1(values, utils, meta); } case 3 /* ObjectType.SET */: { - return mergeSets(values, utils, meta); + return mergeSets$1(values, utils, meta); } case 4 /* ObjectType.MAP */: { - return mergeMaps(values, utils, meta); + return mergeMaps$1(values, utils, meta); } default: { - return mergeOthers(values, utils, meta); + return mergeOthers$1(values, utils, meta); } } } @@ -215,7 +297,7 @@ function mergeUnknowns(values, utils, meta) { * * @param values - The records. */ -function mergeRecords(values, utils, meta) { +function mergeRecords$1(values, utils, meta) { const result = utils.mergeFunctions.mergeRecords(values, utils, meta); if (result === actions.defaultMerge || (utils.useImplicitDefaultMerging && @@ -231,7 +313,7 @@ function mergeRecords(values, utils, meta) { * * @param values - The arrays. */ -function mergeArrays(values, utils, meta) { +function mergeArrays$1(values, utils, meta) { const result = utils.mergeFunctions.mergeArrays(values, utils, meta); if (result === actions.defaultMerge || (utils.useImplicitDefaultMerging && @@ -247,7 +329,7 @@ function mergeArrays(values, utils, meta) { * * @param values - The sets. */ -function mergeSets(values, utils, meta) { +function mergeSets$1(values, utils, meta) { const result = utils.mergeFunctions.mergeSets(values, utils, meta); if (result === actions.defaultMerge || (utils.useImplicitDefaultMerging && @@ -262,7 +344,7 @@ function mergeSets(values, utils, meta) { * * @param values - The maps. */ -function mergeMaps(values, utils, meta) { +function mergeMaps$1(values, utils, meta) { const result = utils.mergeFunctions.mergeMaps(values, utils, meta); if (result === actions.defaultMerge || (utils.useImplicitDefaultMerging && @@ -277,7 +359,7 @@ function mergeMaps(values, utils, meta) { * * @param values - The other things. */ -function mergeOthers(values, utils, meta) { +function mergeOthers$1(values, utils, meta) { const result = utils.mergeFunctions.mergeOthers(values, utils, meta); if (result === actions.defaultMerge || (utils.useImplicitDefaultMerging && @@ -288,13 +370,14 @@ function mergeOthers(values, utils, meta) { } return result; } + /** - * The default strategy to merge records. + * The default strategy to merge records into a target record. * - * @param values - The records. + * @param m_target - The result will be mutated into this record + * @param values - The records (including the target's value if there is one). */ -function defaultMergeRecords(values, utils, meta) { - const result = {}; +function mergeRecords(m_target, values, utils, meta) { /* eslint-disable functional/no-loop-statements, functional/no-conditional-statements -- using a loop here is more performant. */ for (const key of getKeys(values)) { const propValues = []; @@ -310,55 +393,211 @@ function defaultMergeRecords(values, utils, meta) { key, parents: values, }); - const propertyResult = mergeUnknowns(propValues, utils, updatedMeta); - if (propertyResult === actions.skip) { - continue; - } + const propertyTarget = { value: propValues[0] }; + mergeUnknownsInto(propertyTarget, propValues, utils, updatedMeta); if (key === "__proto__") { - Object.defineProperty(result, key, { - value: propertyResult, + Object.defineProperty(m_target, key, { + value: propertyTarget.value, configurable: true, enumerable: true, writable: true, }); } else { - result[key] = propertyResult; + m_target.value[key] = propertyTarget.value; } } /* eslint-enable functional/no-loop-statements, functional/no-conditional-statements */ - return result; } /** - * The default strategy to merge arrays. + * The default strategy to merge arrays into a target array. + * + * @param m_target - The result will be mutated into this array + * @param values - The arrays (including the target's value if there is one). + */ +function mergeArrays(m_target, values) { + m_target.value.push(...values.slice(1).flat()); +} +/** + * The default strategy to merge sets into a target set. + * + * @param m_target - The result will be mutated into this set + * @param values - The sets (including the target's value if there is one). + */ +function mergeSets(m_target, values) { + for (const value of getIterableOfIterables(values.slice(1))) { + m_target.value.add(value); + } +} +/** + * The default strategy to merge maps into a target map. + * + * @param m_target - The result will be mutated into this map + * @param values - The maps (including the target's value if there is one). + */ +function mergeMaps(m_target, values) { + for (const [key, value] of getIterableOfIterables(values.slice(1))) { + m_target.value.set(key, value); + } +} +/** + * Set the target to the last value. + */ +function mergeOthers(m_target, values) { + m_target.value = values[values.length - 1]; +} + +var defaultMergeIntoFunctions = /*#__PURE__*/Object.freeze({ + __proto__: null, + mergeArrays: mergeArrays, + mergeMaps: mergeMaps, + mergeOthers: mergeOthers, + mergeRecords: mergeRecords, + mergeSets: mergeSets +}); + +function deepmergeInto(target, ...objects) { + return void deepmergeIntoCustom({})(target, ...objects); +} +function deepmergeIntoCustom(options, rootMetaData) { + const utils = getIntoUtils(options, customizedDeepmergeInto); + /** + * The customized deepmerge function. + */ + function customizedDeepmergeInto(target, ...objects) { + mergeUnknownsInto({ value: target }, [target, ...objects], utils, rootMetaData); + } + return customizedDeepmergeInto; +} +/** + * The the utils that are available to the merge functions. + * + * @param options - The options the user specified + */ +function getIntoUtils(options, customizedDeepmergeInto) { + var _a; + return { + defaultMergeFunctions: defaultMergeIntoFunctions, + mergeFunctions: { + ...defaultMergeIntoFunctions, + ...Object.fromEntries(Object.entries(options) + .filter(([key, option]) => Object.prototype.hasOwnProperty.call(defaultMergeIntoFunctions, key)) + .map(([key, option]) => option === false + ? [key, mergeOthers] + : [key, option])), + }, + metaDataUpdater: ((_a = options.metaDataUpdater) !== null && _a !== void 0 ? _a : defaultMetaDataUpdater), + deepmergeInto: customizedDeepmergeInto, + actions: actionsInto, + }; +} +/** + * Merge unknown things into a target. + * + * @param m_target - The target to merge into. + * @param values - The values. + */ +function mergeUnknownsInto(m_target, values, utils, meta +// eslint-disable-next-line @typescript-eslint/no-invalid-void-type +) { + if (values.length === 0) { + return; + } + if (values.length === 1) { + return void mergeOthersInto(m_target, values, utils, meta); + } + const type = getObjectType(m_target.value); + // eslint-disable-next-line functional/no-conditional-statements -- add an early escape for better performance. + if (type !== 0 /* ObjectType.NOT */ && type !== 5 /* ObjectType.OTHER */) { + // eslint-disable-next-line functional/no-loop-statements -- using a loop here is more performant than mapping every value and then testing every value. + for (let m_index = 1; m_index < values.length; m_index++) { + if (getObjectType(values[m_index]) === type) { + continue; + } + return void mergeOthersInto(m_target, values, utils, meta); + } + } + switch (type) { + case 1 /* ObjectType.RECORD */: { + return void mergeRecordsInto(m_target, values, utils, meta); + } + case 2 /* ObjectType.ARRAY */: { + return void mergeArraysInto(m_target, values, utils, meta); + } + case 3 /* ObjectType.SET */: { + return void mergeSetsInto(m_target, values, utils, meta); + } + case 4 /* ObjectType.MAP */: { + return void mergeMapsInto(m_target, values, utils, meta); + } + default: { + return void mergeOthersInto(m_target, values, utils, meta); + } + } +} +/** + * Merge records into a target record. + * + * @param m_target - The target to merge into. + * @param values - The records. + */ +function mergeRecordsInto(m_target, values, utils, meta) { + const action = utils.mergeFunctions.mergeRecords(m_target, values, utils, meta); + if (action === actionsInto.defaultMerge) { + utils.defaultMergeFunctions.mergeRecords(m_target, values, utils, meta); + } +} +/** + * Merge arrays into a target array. * + * @param m_target - The target to merge into. * @param values - The arrays. */ -function defaultMergeArrays(values) { - return values.flat(); +function mergeArraysInto(m_target, values, utils, meta) { + const action = utils.mergeFunctions.mergeArrays(m_target, values, utils, meta); + if (action === actionsInto.defaultMerge) { + utils.defaultMergeFunctions.mergeArrays(m_target, values); + } } /** - * The default strategy to merge sets. + * Merge sets into a target set. * + * @param m_target - The target to merge into. * @param values - The sets. */ -function defaultMergeSets(values) { - return new Set(getIterableOfIterables(values)); +function mergeSetsInto(m_target, values, utils, meta) { + const action = utils.mergeFunctions.mergeSets(m_target, values, utils, meta); + if (action === actionsInto.defaultMerge) { + utils.defaultMergeFunctions.mergeSets(m_target, values); + } } /** - * The default strategy to merge maps. + * Merge maps into a target map. * + * @param m_target - The target to merge into. * @param values - The maps. */ -function defaultMergeMaps(values) { - return new Map(getIterableOfIterables(values)); +function mergeMapsInto(m_target, values, utils, meta) { + const action = utils.mergeFunctions.mergeMaps(m_target, values, utils, meta); + if (action === actionsInto.defaultMerge) { + utils.defaultMergeFunctions.mergeMaps(m_target, values); + } } /** - * Get the last value in the given array. + * Merge other things into a target. + * + * @param m_target - The target to merge into. + * @param values - The other things. */ -function leaf(values) { - return values[values.length - 1]; +function mergeOthersInto(m_target, values, utils, meta) { + const action = utils.mergeFunctions.mergeOthers(m_target, values, utils, meta); + if (action === actionsInto.defaultMerge || + m_target.value === actionsInto.defaultMerge) { + utils.defaultMergeFunctions.mergeOthers(m_target, values); + } } exports.deepmerge = deepmerge; exports.deepmergeCustom = deepmergeCustom; +exports.deepmergeInto = deepmergeInto; +exports.deepmergeIntoCustom = deepmergeIntoCustom; diff --git a/dist/node/index.mjs b/dist/node/index.mjs index 6e9ee9de..0df303a7 100644 --- a/dist/node/index.mjs +++ b/dist/node/index.mjs @@ -1,3 +1,24 @@ +/** + * Special values that tell deepmerge to perform a certain action. + */ +const actions = { + defaultMerge: Symbol("deepmerge-ts: default merge"), + skip: Symbol("deepmerge-ts: skip"), +}; +/** + * Special values that tell deepmergeInto to perform a certain action. + */ +const actionsInto = { + defaultMerge: actions.defaultMerge, +}; + +/** + * The default function to update meta data. + */ +function defaultMetaDataUpdater(previousMeta, metaMeta) { + return metaMeta; +} + /** * Get the type of the given object. * @@ -106,26 +127,87 @@ function isRecord(value) { return true; } -const defaultMergeFunctions = { - mergeMaps: defaultMergeMaps, - mergeSets: defaultMergeSets, - mergeArrays: defaultMergeArrays, - mergeRecords: defaultMergeRecords, - mergeOthers: leaf, -}; /** - * Special values that tell deepmerge-ts to perform a certain action. + * The default strategy to merge records. + * + * @param values - The records. */ -const actions = { - defaultMerge: Symbol("deepmerge-ts: default merge"), - skip: Symbol("deepmerge-ts: skip"), -}; +function mergeRecords$2(values, utils, meta) { + const result = {}; + /* eslint-disable functional/no-loop-statements, functional/no-conditional-statements -- using a loop here is more performant. */ + for (const key of getKeys(values)) { + const propValues = []; + for (const value of values) { + if (objectHasProperty(value, key)) { + propValues.push(value[key]); + } + } + if (propValues.length === 0) { + continue; + } + const updatedMeta = utils.metaDataUpdater(meta, { + key, + parents: values, + }); + const propertyResult = mergeUnknowns(propValues, utils, updatedMeta); + if (propertyResult === actions.skip) { + continue; + } + if (key === "__proto__") { + Object.defineProperty(result, key, { + value: propertyResult, + configurable: true, + enumerable: true, + writable: true, + }); + } + else { + result[key] = propertyResult; + } + } + /* eslint-enable functional/no-loop-statements, functional/no-conditional-statements */ + return result; +} /** - * The default function to update meta data. + * The default strategy to merge arrays. + * + * @param values - The arrays. */ -function defaultMetaDataUpdater(previousMeta, metaMeta) { - return metaMeta; +function mergeArrays$2(values) { + return values.flat(); +} +/** + * The default strategy to merge sets. + * + * @param values - The sets. + */ +function mergeSets$2(values) { + return new Set(getIterableOfIterables(values)); +} +/** + * The default strategy to merge maps. + * + * @param values - The maps. + */ +function mergeMaps$2(values) { + return new Map(getIterableOfIterables(values)); } +/** + * Get the last value in the given array. + */ +function mergeOthers$2(values) { + return values[values.length - 1]; +} + +var defaultMergeFunctions = /*#__PURE__*/Object.freeze({ + __proto__: null, + mergeArrays: mergeArrays$2, + mergeMaps: mergeMaps$2, + mergeOthers: mergeOthers$2, + mergeRecords: mergeRecords$2, + mergeSets: mergeSets$2 +}); + /** * Deeply merge objects. * @@ -145,7 +227,7 @@ function deepmergeCustom(options, rootMetaData) { return customizedDeepmerge; } /** - * The the full options with defaults apply. + * The the utils that are available to the merge functions. * * @param options - The options the user specified */ @@ -157,7 +239,9 @@ function getUtils(options, customizedDeepmerge) { ...defaultMergeFunctions, ...Object.fromEntries(Object.entries(options) .filter(([key, option]) => Object.prototype.hasOwnProperty.call(defaultMergeFunctions, key)) - .map(([key, option]) => option === false ? [key, leaf] : [key, option])), + .map(([key, option]) => option === false + ? [key, mergeOthers$2] + : [key, option])), }, metaDataUpdater: ((_a = options.metaDataUpdater) !== null && _a !== void 0 ? _a : defaultMetaDataUpdater), deepmerge: customizedDeepmerge, @@ -175,7 +259,7 @@ function mergeUnknowns(values, utils, meta) { return undefined; } if (values.length === 1) { - return mergeOthers(values, utils, meta); + return mergeOthers$1(values, utils, meta); } const type = getObjectType(values[0]); // eslint-disable-next-line functional/no-conditional-statements -- add an early escape for better performance. @@ -185,24 +269,24 @@ function mergeUnknowns(values, utils, meta) { if (getObjectType(values[m_index]) === type) { continue; } - return mergeOthers(values, utils, meta); + return mergeOthers$1(values, utils, meta); } } switch (type) { case 1 /* ObjectType.RECORD */: { - return mergeRecords(values, utils, meta); + return mergeRecords$1(values, utils, meta); } case 2 /* ObjectType.ARRAY */: { - return mergeArrays(values, utils, meta); + return mergeArrays$1(values, utils, meta); } case 3 /* ObjectType.SET */: { - return mergeSets(values, utils, meta); + return mergeSets$1(values, utils, meta); } case 4 /* ObjectType.MAP */: { - return mergeMaps(values, utils, meta); + return mergeMaps$1(values, utils, meta); } default: { - return mergeOthers(values, utils, meta); + return mergeOthers$1(values, utils, meta); } } } @@ -211,7 +295,7 @@ function mergeUnknowns(values, utils, meta) { * * @param values - The records. */ -function mergeRecords(values, utils, meta) { +function mergeRecords$1(values, utils, meta) { const result = utils.mergeFunctions.mergeRecords(values, utils, meta); if (result === actions.defaultMerge || (utils.useImplicitDefaultMerging && @@ -227,7 +311,7 @@ function mergeRecords(values, utils, meta) { * * @param values - The arrays. */ -function mergeArrays(values, utils, meta) { +function mergeArrays$1(values, utils, meta) { const result = utils.mergeFunctions.mergeArrays(values, utils, meta); if (result === actions.defaultMerge || (utils.useImplicitDefaultMerging && @@ -243,7 +327,7 @@ function mergeArrays(values, utils, meta) { * * @param values - The sets. */ -function mergeSets(values, utils, meta) { +function mergeSets$1(values, utils, meta) { const result = utils.mergeFunctions.mergeSets(values, utils, meta); if (result === actions.defaultMerge || (utils.useImplicitDefaultMerging && @@ -258,7 +342,7 @@ function mergeSets(values, utils, meta) { * * @param values - The maps. */ -function mergeMaps(values, utils, meta) { +function mergeMaps$1(values, utils, meta) { const result = utils.mergeFunctions.mergeMaps(values, utils, meta); if (result === actions.defaultMerge || (utils.useImplicitDefaultMerging && @@ -273,7 +357,7 @@ function mergeMaps(values, utils, meta) { * * @param values - The other things. */ -function mergeOthers(values, utils, meta) { +function mergeOthers$1(values, utils, meta) { const result = utils.mergeFunctions.mergeOthers(values, utils, meta); if (result === actions.defaultMerge || (utils.useImplicitDefaultMerging && @@ -284,13 +368,14 @@ function mergeOthers(values, utils, meta) { } return result; } + /** - * The default strategy to merge records. + * The default strategy to merge records into a target record. * - * @param values - The records. + * @param m_target - The result will be mutated into this record + * @param values - The records (including the target's value if there is one). */ -function defaultMergeRecords(values, utils, meta) { - const result = {}; +function mergeRecords(m_target, values, utils, meta) { /* eslint-disable functional/no-loop-statements, functional/no-conditional-statements -- using a loop here is more performant. */ for (const key of getKeys(values)) { const propValues = []; @@ -306,54 +391,208 @@ function defaultMergeRecords(values, utils, meta) { key, parents: values, }); - const propertyResult = mergeUnknowns(propValues, utils, updatedMeta); - if (propertyResult === actions.skip) { - continue; - } + const propertyTarget = { value: propValues[0] }; + mergeUnknownsInto(propertyTarget, propValues, utils, updatedMeta); if (key === "__proto__") { - Object.defineProperty(result, key, { - value: propertyResult, + Object.defineProperty(m_target, key, { + value: propertyTarget.value, configurable: true, enumerable: true, writable: true, }); } else { - result[key] = propertyResult; + m_target.value[key] = propertyTarget.value; } } /* eslint-enable functional/no-loop-statements, functional/no-conditional-statements */ - return result; } /** - * The default strategy to merge arrays. + * The default strategy to merge arrays into a target array. + * + * @param m_target - The result will be mutated into this array + * @param values - The arrays (including the target's value if there is one). + */ +function mergeArrays(m_target, values) { + m_target.value.push(...values.slice(1).flat()); +} +/** + * The default strategy to merge sets into a target set. * + * @param m_target - The result will be mutated into this set + * @param values - The sets (including the target's value if there is one). + */ +function mergeSets(m_target, values) { + for (const value of getIterableOfIterables(values.slice(1))) { + m_target.value.add(value); + } +} +/** + * The default strategy to merge maps into a target map. + * + * @param m_target - The result will be mutated into this map + * @param values - The maps (including the target's value if there is one). + */ +function mergeMaps(m_target, values) { + for (const [key, value] of getIterableOfIterables(values.slice(1))) { + m_target.value.set(key, value); + } +} +/** + * Set the target to the last value. + */ +function mergeOthers(m_target, values) { + m_target.value = values[values.length - 1]; +} + +var defaultMergeIntoFunctions = /*#__PURE__*/Object.freeze({ + __proto__: null, + mergeArrays: mergeArrays, + mergeMaps: mergeMaps, + mergeOthers: mergeOthers, + mergeRecords: mergeRecords, + mergeSets: mergeSets +}); + +function deepmergeInto(target, ...objects) { + return void deepmergeIntoCustom({})(target, ...objects); +} +function deepmergeIntoCustom(options, rootMetaData) { + const utils = getIntoUtils(options, customizedDeepmergeInto); + /** + * The customized deepmerge function. + */ + function customizedDeepmergeInto(target, ...objects) { + mergeUnknownsInto({ value: target }, [target, ...objects], utils, rootMetaData); + } + return customizedDeepmergeInto; +} +/** + * The the utils that are available to the merge functions. + * + * @param options - The options the user specified + */ +function getIntoUtils(options, customizedDeepmergeInto) { + var _a; + return { + defaultMergeFunctions: defaultMergeIntoFunctions, + mergeFunctions: { + ...defaultMergeIntoFunctions, + ...Object.fromEntries(Object.entries(options) + .filter(([key, option]) => Object.prototype.hasOwnProperty.call(defaultMergeIntoFunctions, key)) + .map(([key, option]) => option === false + ? [key, mergeOthers] + : [key, option])), + }, + metaDataUpdater: ((_a = options.metaDataUpdater) !== null && _a !== void 0 ? _a : defaultMetaDataUpdater), + deepmergeInto: customizedDeepmergeInto, + actions: actionsInto, + }; +} +/** + * Merge unknown things into a target. + * + * @param m_target - The target to merge into. + * @param values - The values. + */ +function mergeUnknownsInto(m_target, values, utils, meta +// eslint-disable-next-line @typescript-eslint/no-invalid-void-type +) { + if (values.length === 0) { + return; + } + if (values.length === 1) { + return void mergeOthersInto(m_target, values, utils, meta); + } + const type = getObjectType(m_target.value); + // eslint-disable-next-line functional/no-conditional-statements -- add an early escape for better performance. + if (type !== 0 /* ObjectType.NOT */ && type !== 5 /* ObjectType.OTHER */) { + // eslint-disable-next-line functional/no-loop-statements -- using a loop here is more performant than mapping every value and then testing every value. + for (let m_index = 1; m_index < values.length; m_index++) { + if (getObjectType(values[m_index]) === type) { + continue; + } + return void mergeOthersInto(m_target, values, utils, meta); + } + } + switch (type) { + case 1 /* ObjectType.RECORD */: { + return void mergeRecordsInto(m_target, values, utils, meta); + } + case 2 /* ObjectType.ARRAY */: { + return void mergeArraysInto(m_target, values, utils, meta); + } + case 3 /* ObjectType.SET */: { + return void mergeSetsInto(m_target, values, utils, meta); + } + case 4 /* ObjectType.MAP */: { + return void mergeMapsInto(m_target, values, utils, meta); + } + default: { + return void mergeOthersInto(m_target, values, utils, meta); + } + } +} +/** + * Merge records into a target record. + * + * @param m_target - The target to merge into. + * @param values - The records. + */ +function mergeRecordsInto(m_target, values, utils, meta) { + const action = utils.mergeFunctions.mergeRecords(m_target, values, utils, meta); + if (action === actionsInto.defaultMerge) { + utils.defaultMergeFunctions.mergeRecords(m_target, values, utils, meta); + } +} +/** + * Merge arrays into a target array. + * + * @param m_target - The target to merge into. * @param values - The arrays. */ -function defaultMergeArrays(values) { - return values.flat(); +function mergeArraysInto(m_target, values, utils, meta) { + const action = utils.mergeFunctions.mergeArrays(m_target, values, utils, meta); + if (action === actionsInto.defaultMerge) { + utils.defaultMergeFunctions.mergeArrays(m_target, values); + } } /** - * The default strategy to merge sets. + * Merge sets into a target set. * + * @param m_target - The target to merge into. * @param values - The sets. */ -function defaultMergeSets(values) { - return new Set(getIterableOfIterables(values)); +function mergeSetsInto(m_target, values, utils, meta) { + const action = utils.mergeFunctions.mergeSets(m_target, values, utils, meta); + if (action === actionsInto.defaultMerge) { + utils.defaultMergeFunctions.mergeSets(m_target, values); + } } /** - * The default strategy to merge maps. + * Merge maps into a target map. * + * @param m_target - The target to merge into. * @param values - The maps. */ -function defaultMergeMaps(values) { - return new Map(getIterableOfIterables(values)); +function mergeMapsInto(m_target, values, utils, meta) { + const action = utils.mergeFunctions.mergeMaps(m_target, values, utils, meta); + if (action === actionsInto.defaultMerge) { + utils.defaultMergeFunctions.mergeMaps(m_target, values); + } } /** - * Get the last value in the given array. + * Merge other things into a target. + * + * @param m_target - The target to merge into. + * @param values - The other things. */ -function leaf(values) { - return values[values.length - 1]; +function mergeOthersInto(m_target, values, utils, meta) { + const action = utils.mergeFunctions.mergeOthers(m_target, values, utils, meta); + if (action === actionsInto.defaultMerge || + m_target.value === actionsInto.defaultMerge) { + utils.defaultMergeFunctions.mergeOthers(m_target, values); + } } -export { deepmerge, deepmergeCustom }; +export { deepmerge, deepmergeCustom, deepmergeInto, deepmergeIntoCustom }; diff --git a/dist/node/types/current/index.d.cts b/dist/node/types/current/index.d.cts index 4ee457d1..87fd61bb 100644 --- a/dist/node/types/current/index.d.cts +++ b/dist/node/types/current/index.d.cts @@ -2,7 +2,7 @@ * Flatten a complex type such as a union or intersection of objects into a * single object. */ -type FlatternAlias = { +type FlatternAlias = Is extends true ? T : { [P in keyof T]: T[P]; } & {}; /** @@ -309,10 +309,96 @@ type GetDeepMergeMergeFunctionsURIs ? PMF["DeepMergeOthersURI"] : DeepMergeLeafURI; }>; +/** + * The default merge functions. + */ +type MergeFunctions$1 = { + mergeRecords: typeof mergeRecords$1; + mergeArrays: typeof mergeArrays$1; + mergeSets: typeof mergeSets$1; + mergeMaps: typeof mergeMaps$1; + mergeOthers: typeof mergeOthers$1; +}; +/** + * The default strategy to merge records into a target record. + * + * @param m_target - The result will be mutated into this record + * @param values - The records (including the target's value if there is one). + */ +declare function mergeRecords$1>, U extends DeepMergeMergeIntoFunctionUtils, M, MM extends DeepMergeBuiltInMetaData>(m_target: Reference>, values: Ts, utils: U, meta: M | undefined): void; +/** + * The default strategy to merge arrays into a target array. + * + * @param m_target - The result will be mutated into this array + * @param values - The arrays (including the target's value if there is one). + */ +declare function mergeArrays$1>>(m_target: Reference, values: Ts): void; +/** + * The default strategy to merge sets into a target set. + * + * @param m_target - The result will be mutated into this set + * @param values - The sets (including the target's value if there is one). + */ +declare function mergeSets$1>>>(m_target: Reference>, values: Ts): void; +/** + * The default strategy to merge maps into a target map. + * + * @param m_target - The result will be mutated into this map + * @param values - The maps (including the target's value if there is one). + */ +declare function mergeMaps$1>>>(m_target: Reference>, values: Ts): void; +/** + * Set the target to the last value. + */ +declare function mergeOthers$1>(m_target: Reference, values: Ts): void; + +/** + * The default merge functions. + */ +type MergeFunctions = { + mergeRecords: typeof mergeRecords; + mergeArrays: typeof mergeArrays; + mergeSets: typeof mergeSets; + mergeMaps: typeof mergeMaps; + mergeOthers: typeof mergeOthers; +}; +/** + * The default strategy to merge records. + * + * @param values - The records. + */ +declare function mergeRecords>, U extends DeepMergeMergeFunctionUtils, MF extends DeepMergeMergeFunctionsURIs, M, MM extends DeepMergeBuiltInMetaData>(values: Ts, utils: U, meta: M | undefined): DeepMergeRecordsDefaultHKT; +/** + * The default strategy to merge arrays. + * + * @param values - The arrays. + */ +declare function mergeArrays>, MF extends DeepMergeMergeFunctionsURIs, M>(values: Ts): DeepMergeArraysDefaultHKT; +/** + * The default strategy to merge sets. + * + * @param values - The sets. + */ +declare function mergeSets>>>(values: Ts): DeepMergeSetsDefaultHKT; +/** + * The default strategy to merge maps. + * + * @param values - The maps. + */ +declare function mergeMaps>>>(values: Ts): DeepMergeMapsDefaultHKT; +/** + * Get the last value in the given array. + */ +declare function mergeOthers>(values: Ts): unknown; + /** * The options the user can pass to customize deepmerge. */ type DeepMergeOptions> = DeepMergeBuiltInMetaData> = Partial>; +/** + * The options the user can pass to customize deepmergeInto. + */ +type DeepMergeIntoOptions> = DeepMergeBuiltInMetaData> = Partial>; type MetaDataUpdater = (previousMeta: M | undefined, metaMeta: Readonly>) => M; /** * All the options the user can pass to customize deepmerge. @@ -326,6 +412,23 @@ type DeepMergeOptionsFull = Reado metaDataUpdater: MetaDataUpdater; enableImplicitDefaultMerging: boolean; }>; +/** + * All the options the user can pass to customize deepmergeInto. + */ +type DeepMergeIntoOptionsFull = Readonly<{ + mergeRecords: DeepMergeMergeIntoFunctions["mergeRecords"] | false; + mergeArrays: DeepMergeMergeIntoFunctions["mergeArrays"] | false; + mergeMaps: DeepMergeMergeIntoFunctions["mergeMaps"] | false; + mergeSets: DeepMergeMergeIntoFunctions["mergeSets"] | false; + mergeOthers: DeepMergeMergeIntoFunctions["mergeOthers"]; + metaDataUpdater: MetaDataUpdater; +}>; +/** + * An object that has a reference to a value. + */ +type Reference = { + value: T; +}; /** * All the merge functions that deepmerge uses. */ @@ -336,12 +439,23 @@ type DeepMergeMergeFunctions = Readon mergeSets: >>, U extends DeepMergeMergeFunctionUtils>(values: Ts, utils: U, meta: M | undefined) => unknown; mergeOthers: , U extends DeepMergeMergeFunctionUtils>(values: Ts, utils: U, meta: M | undefined) => unknown; }>; +type DeepMergeMergeIntoFunctionsReturnType = void | symbol; +/** + * All the merge functions that deepmerge uses. + */ +type DeepMergeMergeIntoFunctions = Readonly<{ + mergeRecords: >>, U extends DeepMergeMergeIntoFunctionUtils>(m_target: Reference>, values: Ts, utils: U, meta: M | undefined) => DeepMergeMergeIntoFunctionsReturnType; + mergeArrays: >, U extends DeepMergeMergeIntoFunctionUtils>(m_target: Reference, values: Ts, utils: U, meta: M | undefined) => DeepMergeMergeIntoFunctionsReturnType; + mergeMaps: >>, U extends DeepMergeMergeIntoFunctionUtils>(m_target: Reference>, values: Ts, utils: U, meta: M | undefined) => DeepMergeMergeIntoFunctionsReturnType; + mergeSets: >>, U extends DeepMergeMergeIntoFunctionUtils>(m_target: Reference>, values: Ts, utils: U, meta: M | undefined) => DeepMergeMergeIntoFunctionsReturnType; + mergeOthers: , U extends DeepMergeMergeIntoFunctionUtils>(m_target: Reference, values: Ts, utils: U, meta: M | undefined) => DeepMergeMergeIntoFunctionsReturnType; +}>; /** * The utils provided to the merge functions. */ type DeepMergeMergeFunctionUtils = Readonly<{ mergeFunctions: DeepMergeMergeFunctions; - defaultMergeFunctions: DeepMergeMergeFunctionsDefaults; + defaultMergeFunctions: MergeFunctions; metaDataUpdater: MetaDataUpdater; deepmerge: >(...values: Ts) => unknown; useImplicitDefaultMerging: boolean; @@ -350,18 +464,19 @@ type DeepMergeMergeFunctionUtils skip: symbol; }>; }>; - -declare const defaultMergeFunctions: { - readonly mergeMaps: typeof defaultMergeMaps; - readonly mergeSets: typeof defaultMergeSets; - readonly mergeArrays: typeof defaultMergeArrays; - readonly mergeRecords: typeof defaultMergeRecords; - readonly mergeOthers: typeof leaf; -}; /** - * The default merge functions. + * The utils provided to the merge functions. */ -type DeepMergeMergeFunctionsDefaults = typeof defaultMergeFunctions; +type DeepMergeMergeIntoFunctionUtils = Readonly<{ + mergeFunctions: DeepMergeMergeIntoFunctions; + defaultMergeFunctions: MergeFunctions$1; + metaDataUpdater: MetaDataUpdater; + deepmergeInto: >(target: Target, ...values: Ts) => void; + actions: Readonly<{ + defaultMerge: symbol; + }>; +}>; + /** * Deeply merge objects. * @@ -381,33 +496,36 @@ declare function deepmergeCustom, MetaData, MetaMetaData extends DeepMergeBuiltInMetaData = DeepMergeBuiltInMetaData>(options: DeepMergeOptions, rootMetaData?: MetaData): >(...objects: Ts) => DeepMergeHKT, MetaData>; + /** - * The default strategy to merge records. + * Deeply merge objects into a target. * - * @param values - The records. + * @param target - This object will be mutated with the merge result. + * @param objects - The objects to merge into the target. */ -declare function defaultMergeRecords>, U extends DeepMergeMergeFunctionUtils, MF extends DeepMergeMergeFunctionsURIs, M, MM extends DeepMergeBuiltInMetaData>(values: Ts, utils: U, meta: M | undefined): DeepMergeRecordsDefaultHKT; +declare function deepmergeInto(target: T, ...objects: ReadonlyArray): void; /** - * The default strategy to merge arrays. + * Deeply merge objects into a target. * - * @param values - The arrays. + * @param target - This object will be mutated with the merge result. + * @param objects - The objects to merge into the target. */ -declare function defaultMergeArrays>, MF extends DeepMergeMergeFunctionsURIs, M>(values: Ts): DeepMergeArraysDefaultHKT; +declare function deepmergeInto>(target: Target, ...objects: Ts): asserts target is FlatternAlias>; /** - * The default strategy to merge sets. + * Deeply merge two or more objects using the given options. * - * @param values - The sets. + * @param options - The options on how to customize the merge function. */ -declare function defaultMergeSets>>>(values: Ts): DeepMergeSetsDefaultHKT; +declare function deepmergeIntoCustom(options: DeepMergeIntoOptions): >(target: Target, ...objects: Ts) => void; /** - * The default strategy to merge maps. + * Deeply merge two or more objects using the given options and meta data. * - * @param values - The maps. - */ -declare function defaultMergeMaps>>>(values: Ts): DeepMergeMapsDefaultHKT; -/** - * Get the last value in the given array. + * @param options - The options on how to customize the merge function. + * @param rootMetaData - The meta data passed to the root items' being merged. */ -declare function leaf>(values: Ts): unknown; +declare function deepmergeIntoCustom(options: DeepMergeIntoOptions, rootMetaData?: MetaData): >(target: Target, ...objects: Ts) => void; -export { DeepMergeArraysDefaultHKT, DeepMergeBuiltInMetaData, DeepMergeHKT, DeepMergeLeaf, DeepMergeLeafHKT, DeepMergeLeafURI, DeepMergeMapsDefaultHKT, DeepMergeMergeFunctionURItoKind, DeepMergeMergeFunctionUtils, DeepMergeMergeFunctionsDefaultURIs, DeepMergeMergeFunctionsDefaults, DeepMergeMergeFunctionsURIs, DeepMergeOptions, DeepMergeRecordsDefaultHKT, DeepMergeSetsDefaultHKT, deepmerge, deepmergeCustom }; +export { DeepMergeArraysDefaultHKT, DeepMergeBuiltInMetaData, DeepMergeHKT, DeepMergeIntoOptions, DeepMergeLeaf, DeepMergeLeafHKT, DeepMergeLeafURI, DeepMergeMapsDefaultHKT, DeepMergeMergeFunctionURItoKind, DeepMergeMergeFunctionUtils, DeepMergeMergeFunctionsDefaultURIs, MergeFunctions as DeepMergeMergeFunctionsDefaults, DeepMergeMergeFunctionsURIs, DeepMergeMergeIntoFunctionUtils, MergeFunctions$1 as DeepMergeMergeIntoFunctionsDefaults, DeepMergeOptions, DeepMergeRecordsDefaultHKT, DeepMergeSetsDefaultHKT, Reference as DeepMergeValueReference, deepmerge, deepmergeCustom, deepmergeInto, deepmergeIntoCustom }; diff --git a/dist/node/types/current/index.d.mts b/dist/node/types/current/index.d.mts index 4ee457d1..87fd61bb 100644 --- a/dist/node/types/current/index.d.mts +++ b/dist/node/types/current/index.d.mts @@ -2,7 +2,7 @@ * Flatten a complex type such as a union or intersection of objects into a * single object. */ -type FlatternAlias = { +type FlatternAlias = Is extends true ? T : { [P in keyof T]: T[P]; } & {}; /** @@ -309,10 +309,96 @@ type GetDeepMergeMergeFunctionsURIs ? PMF["DeepMergeOthersURI"] : DeepMergeLeafURI; }>; +/** + * The default merge functions. + */ +type MergeFunctions$1 = { + mergeRecords: typeof mergeRecords$1; + mergeArrays: typeof mergeArrays$1; + mergeSets: typeof mergeSets$1; + mergeMaps: typeof mergeMaps$1; + mergeOthers: typeof mergeOthers$1; +}; +/** + * The default strategy to merge records into a target record. + * + * @param m_target - The result will be mutated into this record + * @param values - The records (including the target's value if there is one). + */ +declare function mergeRecords$1>, U extends DeepMergeMergeIntoFunctionUtils, M, MM extends DeepMergeBuiltInMetaData>(m_target: Reference>, values: Ts, utils: U, meta: M | undefined): void; +/** + * The default strategy to merge arrays into a target array. + * + * @param m_target - The result will be mutated into this array + * @param values - The arrays (including the target's value if there is one). + */ +declare function mergeArrays$1>>(m_target: Reference, values: Ts): void; +/** + * The default strategy to merge sets into a target set. + * + * @param m_target - The result will be mutated into this set + * @param values - The sets (including the target's value if there is one). + */ +declare function mergeSets$1>>>(m_target: Reference>, values: Ts): void; +/** + * The default strategy to merge maps into a target map. + * + * @param m_target - The result will be mutated into this map + * @param values - The maps (including the target's value if there is one). + */ +declare function mergeMaps$1>>>(m_target: Reference>, values: Ts): void; +/** + * Set the target to the last value. + */ +declare function mergeOthers$1>(m_target: Reference, values: Ts): void; + +/** + * The default merge functions. + */ +type MergeFunctions = { + mergeRecords: typeof mergeRecords; + mergeArrays: typeof mergeArrays; + mergeSets: typeof mergeSets; + mergeMaps: typeof mergeMaps; + mergeOthers: typeof mergeOthers; +}; +/** + * The default strategy to merge records. + * + * @param values - The records. + */ +declare function mergeRecords>, U extends DeepMergeMergeFunctionUtils, MF extends DeepMergeMergeFunctionsURIs, M, MM extends DeepMergeBuiltInMetaData>(values: Ts, utils: U, meta: M | undefined): DeepMergeRecordsDefaultHKT; +/** + * The default strategy to merge arrays. + * + * @param values - The arrays. + */ +declare function mergeArrays>, MF extends DeepMergeMergeFunctionsURIs, M>(values: Ts): DeepMergeArraysDefaultHKT; +/** + * The default strategy to merge sets. + * + * @param values - The sets. + */ +declare function mergeSets>>>(values: Ts): DeepMergeSetsDefaultHKT; +/** + * The default strategy to merge maps. + * + * @param values - The maps. + */ +declare function mergeMaps>>>(values: Ts): DeepMergeMapsDefaultHKT; +/** + * Get the last value in the given array. + */ +declare function mergeOthers>(values: Ts): unknown; + /** * The options the user can pass to customize deepmerge. */ type DeepMergeOptions> = DeepMergeBuiltInMetaData> = Partial>; +/** + * The options the user can pass to customize deepmergeInto. + */ +type DeepMergeIntoOptions> = DeepMergeBuiltInMetaData> = Partial>; type MetaDataUpdater = (previousMeta: M | undefined, metaMeta: Readonly>) => M; /** * All the options the user can pass to customize deepmerge. @@ -326,6 +412,23 @@ type DeepMergeOptionsFull = Reado metaDataUpdater: MetaDataUpdater; enableImplicitDefaultMerging: boolean; }>; +/** + * All the options the user can pass to customize deepmergeInto. + */ +type DeepMergeIntoOptionsFull = Readonly<{ + mergeRecords: DeepMergeMergeIntoFunctions["mergeRecords"] | false; + mergeArrays: DeepMergeMergeIntoFunctions["mergeArrays"] | false; + mergeMaps: DeepMergeMergeIntoFunctions["mergeMaps"] | false; + mergeSets: DeepMergeMergeIntoFunctions["mergeSets"] | false; + mergeOthers: DeepMergeMergeIntoFunctions["mergeOthers"]; + metaDataUpdater: MetaDataUpdater; +}>; +/** + * An object that has a reference to a value. + */ +type Reference = { + value: T; +}; /** * All the merge functions that deepmerge uses. */ @@ -336,12 +439,23 @@ type DeepMergeMergeFunctions = Readon mergeSets: >>, U extends DeepMergeMergeFunctionUtils>(values: Ts, utils: U, meta: M | undefined) => unknown; mergeOthers: , U extends DeepMergeMergeFunctionUtils>(values: Ts, utils: U, meta: M | undefined) => unknown; }>; +type DeepMergeMergeIntoFunctionsReturnType = void | symbol; +/** + * All the merge functions that deepmerge uses. + */ +type DeepMergeMergeIntoFunctions = Readonly<{ + mergeRecords: >>, U extends DeepMergeMergeIntoFunctionUtils>(m_target: Reference>, values: Ts, utils: U, meta: M | undefined) => DeepMergeMergeIntoFunctionsReturnType; + mergeArrays: >, U extends DeepMergeMergeIntoFunctionUtils>(m_target: Reference, values: Ts, utils: U, meta: M | undefined) => DeepMergeMergeIntoFunctionsReturnType; + mergeMaps: >>, U extends DeepMergeMergeIntoFunctionUtils>(m_target: Reference>, values: Ts, utils: U, meta: M | undefined) => DeepMergeMergeIntoFunctionsReturnType; + mergeSets: >>, U extends DeepMergeMergeIntoFunctionUtils>(m_target: Reference>, values: Ts, utils: U, meta: M | undefined) => DeepMergeMergeIntoFunctionsReturnType; + mergeOthers: , U extends DeepMergeMergeIntoFunctionUtils>(m_target: Reference, values: Ts, utils: U, meta: M | undefined) => DeepMergeMergeIntoFunctionsReturnType; +}>; /** * The utils provided to the merge functions. */ type DeepMergeMergeFunctionUtils = Readonly<{ mergeFunctions: DeepMergeMergeFunctions; - defaultMergeFunctions: DeepMergeMergeFunctionsDefaults; + defaultMergeFunctions: MergeFunctions; metaDataUpdater: MetaDataUpdater; deepmerge: >(...values: Ts) => unknown; useImplicitDefaultMerging: boolean; @@ -350,18 +464,19 @@ type DeepMergeMergeFunctionUtils skip: symbol; }>; }>; - -declare const defaultMergeFunctions: { - readonly mergeMaps: typeof defaultMergeMaps; - readonly mergeSets: typeof defaultMergeSets; - readonly mergeArrays: typeof defaultMergeArrays; - readonly mergeRecords: typeof defaultMergeRecords; - readonly mergeOthers: typeof leaf; -}; /** - * The default merge functions. + * The utils provided to the merge functions. */ -type DeepMergeMergeFunctionsDefaults = typeof defaultMergeFunctions; +type DeepMergeMergeIntoFunctionUtils = Readonly<{ + mergeFunctions: DeepMergeMergeIntoFunctions; + defaultMergeFunctions: MergeFunctions$1; + metaDataUpdater: MetaDataUpdater; + deepmergeInto: >(target: Target, ...values: Ts) => void; + actions: Readonly<{ + defaultMerge: symbol; + }>; +}>; + /** * Deeply merge objects. * @@ -381,33 +496,36 @@ declare function deepmergeCustom, MetaData, MetaMetaData extends DeepMergeBuiltInMetaData = DeepMergeBuiltInMetaData>(options: DeepMergeOptions, rootMetaData?: MetaData): >(...objects: Ts) => DeepMergeHKT, MetaData>; + /** - * The default strategy to merge records. + * Deeply merge objects into a target. * - * @param values - The records. + * @param target - This object will be mutated with the merge result. + * @param objects - The objects to merge into the target. */ -declare function defaultMergeRecords>, U extends DeepMergeMergeFunctionUtils, MF extends DeepMergeMergeFunctionsURIs, M, MM extends DeepMergeBuiltInMetaData>(values: Ts, utils: U, meta: M | undefined): DeepMergeRecordsDefaultHKT; +declare function deepmergeInto(target: T, ...objects: ReadonlyArray): void; /** - * The default strategy to merge arrays. + * Deeply merge objects into a target. * - * @param values - The arrays. + * @param target - This object will be mutated with the merge result. + * @param objects - The objects to merge into the target. */ -declare function defaultMergeArrays>, MF extends DeepMergeMergeFunctionsURIs, M>(values: Ts): DeepMergeArraysDefaultHKT; +declare function deepmergeInto>(target: Target, ...objects: Ts): asserts target is FlatternAlias>; /** - * The default strategy to merge sets. + * Deeply merge two or more objects using the given options. * - * @param values - The sets. + * @param options - The options on how to customize the merge function. */ -declare function defaultMergeSets>>>(values: Ts): DeepMergeSetsDefaultHKT; +declare function deepmergeIntoCustom(options: DeepMergeIntoOptions): >(target: Target, ...objects: Ts) => void; /** - * The default strategy to merge maps. + * Deeply merge two or more objects using the given options and meta data. * - * @param values - The maps. - */ -declare function defaultMergeMaps>>>(values: Ts): DeepMergeMapsDefaultHKT; -/** - * Get the last value in the given array. + * @param options - The options on how to customize the merge function. + * @param rootMetaData - The meta data passed to the root items' being merged. */ -declare function leaf>(values: Ts): unknown; +declare function deepmergeIntoCustom(options: DeepMergeIntoOptions, rootMetaData?: MetaData): >(target: Target, ...objects: Ts) => void; -export { DeepMergeArraysDefaultHKT, DeepMergeBuiltInMetaData, DeepMergeHKT, DeepMergeLeaf, DeepMergeLeafHKT, DeepMergeLeafURI, DeepMergeMapsDefaultHKT, DeepMergeMergeFunctionURItoKind, DeepMergeMergeFunctionUtils, DeepMergeMergeFunctionsDefaultURIs, DeepMergeMergeFunctionsDefaults, DeepMergeMergeFunctionsURIs, DeepMergeOptions, DeepMergeRecordsDefaultHKT, DeepMergeSetsDefaultHKT, deepmerge, deepmergeCustom }; +export { DeepMergeArraysDefaultHKT, DeepMergeBuiltInMetaData, DeepMergeHKT, DeepMergeIntoOptions, DeepMergeLeaf, DeepMergeLeafHKT, DeepMergeLeafURI, DeepMergeMapsDefaultHKT, DeepMergeMergeFunctionURItoKind, DeepMergeMergeFunctionUtils, DeepMergeMergeFunctionsDefaultURIs, MergeFunctions as DeepMergeMergeFunctionsDefaults, DeepMergeMergeFunctionsURIs, DeepMergeMergeIntoFunctionUtils, MergeFunctions$1 as DeepMergeMergeIntoFunctionsDefaults, DeepMergeOptions, DeepMergeRecordsDefaultHKT, DeepMergeSetsDefaultHKT, Reference as DeepMergeValueReference, deepmerge, deepmergeCustom, deepmergeInto, deepmergeIntoCustom };