[d24f17c] | 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 */ |
---|