1 | import { ObjectElement } from 'minim';
|
---|
2 | import { isObjectElement, isArrayElement } from "./predicates/index.mjs";
|
---|
3 | import { cloneDeep, cloneShallow } from "./clone/index.mjs";
|
---|
4 | import toValue from "./transformers/serializers/value/index.mjs";
|
---|
5 | /* eslint-disable @typescript-eslint/no-use-before-define */
|
---|
6 | const emptyElement = element => {
|
---|
7 | const meta = cloneDeep(element.meta);
|
---|
8 | const attributes = cloneDeep(element.attributes);
|
---|
9 |
|
---|
10 | // @ts-ignore
|
---|
11 | return new element.constructor(undefined, meta, attributes);
|
---|
12 | };
|
---|
13 | const cloneUnlessOtherwiseSpecified = (element, options) => options.clone && options.isMergeableElement(element) ? deepmerge(emptyElement(element), element, options) : element;
|
---|
14 | const getMergeFunction = (keyElement, options) => {
|
---|
15 | if (typeof options.customMerge !== 'function') {
|
---|
16 | return deepmerge;
|
---|
17 | }
|
---|
18 | const customMerge = options.customMerge(keyElement, options);
|
---|
19 | return typeof customMerge === 'function' ? customMerge : deepmerge;
|
---|
20 | };
|
---|
21 | const mergeArrayElement = (targetElement, sourceElement, options) => targetElement.concat(sourceElement)['fantasy-land/map'](item => cloneUnlessOtherwiseSpecified(item, options));
|
---|
22 | const mergeObjectElement = (targetElement, sourceElement, options) => {
|
---|
23 | const destination = isObjectElement(targetElement) ? emptyElement(targetElement) : emptyElement(sourceElement);
|
---|
24 | if (isObjectElement(targetElement)) {
|
---|
25 | targetElement.forEach((value, key, member) => {
|
---|
26 | const clonedMember = cloneShallow(member);
|
---|
27 | clonedMember.value = cloneUnlessOtherwiseSpecified(value, options);
|
---|
28 | destination.content.push(clonedMember);
|
---|
29 | });
|
---|
30 | }
|
---|
31 | sourceElement.forEach((value, key, member) => {
|
---|
32 | const keyValue = toValue(key);
|
---|
33 | let clonedMember;
|
---|
34 | if (isObjectElement(targetElement) && targetElement.hasKey(keyValue) && options.isMergeableElement(value)) {
|
---|
35 | const targetValue = targetElement.get(keyValue);
|
---|
36 | clonedMember = cloneShallow(member);
|
---|
37 | clonedMember.value = getMergeFunction(key, options)(targetValue, value);
|
---|
38 | } else {
|
---|
39 | clonedMember = cloneShallow(member);
|
---|
40 | clonedMember.value = cloneUnlessOtherwiseSpecified(value, options);
|
---|
41 | }
|
---|
42 | destination.remove(keyValue);
|
---|
43 | destination.content.push(clonedMember);
|
---|
44 | });
|
---|
45 | return destination;
|
---|
46 | };
|
---|
47 | export default function deepmerge(targetElement, sourceElement, options) {
|
---|
48 | var _mergedOptions$isMerg, _mergedOptions$arrayE, _mergedOptions$object;
|
---|
49 | const defaultOptions = {
|
---|
50 | clone: true,
|
---|
51 | isMergeableElement: element => isObjectElement(element) || isArrayElement(element),
|
---|
52 | arrayElementMerge: mergeArrayElement,
|
---|
53 | objectElementMerge: mergeObjectElement,
|
---|
54 | customMerge: undefined
|
---|
55 | };
|
---|
56 | const mergedOptions = {
|
---|
57 | ...defaultOptions,
|
---|
58 | ...options
|
---|
59 | };
|
---|
60 | mergedOptions.isMergeableElement = (_mergedOptions$isMerg = mergedOptions.isMergeableElement) !== null && _mergedOptions$isMerg !== void 0 ? _mergedOptions$isMerg : defaultOptions.isMergeableElement;
|
---|
61 | mergedOptions.arrayElementMerge = (_mergedOptions$arrayE = mergedOptions.arrayElementMerge) !== null && _mergedOptions$arrayE !== void 0 ? _mergedOptions$arrayE : defaultOptions.arrayElementMerge;
|
---|
62 | mergedOptions.objectElementMerge = (_mergedOptions$object = mergedOptions.objectElementMerge) !== null && _mergedOptions$object !== void 0 ? _mergedOptions$object : defaultOptions.objectElementMerge;
|
---|
63 | const sourceIsArrayElement = isArrayElement(sourceElement);
|
---|
64 | const targetIsArrayElement = isArrayElement(targetElement);
|
---|
65 | const sourceAndTargetTypesMatch = sourceIsArrayElement === targetIsArrayElement;
|
---|
66 | if (!sourceAndTargetTypesMatch) {
|
---|
67 | return cloneUnlessOtherwiseSpecified(sourceElement, mergedOptions);
|
---|
68 | }
|
---|
69 | if (sourceIsArrayElement && typeof mergedOptions.arrayElementMerge === 'function') {
|
---|
70 | return mergedOptions.arrayElementMerge(targetElement, sourceElement, mergedOptions);
|
---|
71 | }
|
---|
72 | return mergedOptions.objectElementMerge(targetElement, sourceElement, mergedOptions);
|
---|
73 | }
|
---|
74 | deepmerge.all = (list, options) => {
|
---|
75 | if (!Array.isArray(list)) {
|
---|
76 | throw new TypeError('First argument of deepmerge should be an array.');
|
---|
77 | }
|
---|
78 | if (list.length === 0) {
|
---|
79 | return new ObjectElement();
|
---|
80 | }
|
---|
81 | return list.reduce((target, source) => {
|
---|
82 | return deepmerge(target, source, options);
|
---|
83 | }, emptyElement(list[0]));
|
---|
84 | };
|
---|
85 | /* eslint-enable @typescript-eslint/no-use-before-define */ |
---|