UNPKG

@handy-common-utils/misc-utils

Version:
109 lines 4.34 kB
"use strict"; /* eslint-disable unicorn/no-array-callback-reference */ /* eslint-disable unicorn/prefer-spread */ Object.defineProperty(exports, "__esModule", { value: true }); exports.merge = merge; const isObject = (value) => { const type = typeof value; return value != null && (type === 'object' || type === 'function') && !Array.isArray(value); }; const isPlainObject = (value) => { if (!isObject(value) || Object.prototype.toString.call(value) !== '[object Object]') { return false; } const proto = Object.getPrototypeOf(value); if (proto === null) { return true; } const Ctor = Object.prototype.hasOwnProperty.call(proto, 'constructor') && proto.constructor; return typeof Ctor === 'function' && Ctor instanceof Ctor && Function.prototype.toString.call(Ctor) === Function.prototype.toString.call(Object); }; /** * Recursively merges properties of one or more source objects into a destination object. * * @param options - Customizes the merge behavior. * @param destination - The object to merge properties into. It will be mutated unless `options.immutable` is true. * @param sources - The source objects. * @returns The merged object. */ function merge(options, destination, ...sources) { const finalOptions = Object.assign({ immutable: false, array: 'merge', set: 'replace' }, options); const deepClone = (value) => { if (!isObject(value)) { return value; } if (value instanceof Set) { return new Set(Array.from(value.values()).map(deepClone)); } if (Array.isArray(value)) { return value.map(deepClone); } if (isPlainObject(value)) { const newObj = {}; for (const key in value) { if (Object.prototype.hasOwnProperty.call(value, key)) { newObj[key] = deepClone(value[key]); } } return newObj; } // For other complex objects (like Date, RegExp, class instances), return them as is. return value; }; let output = finalOptions.immutable ? deepClone(destination) : destination; const baseMerge = (target, source) => { if (!isObject(target) || !isObject(source)) { return; } Object.keys(source).forEach(key => { const targetValue = target[key]; const sourceValue = source[key]; if (sourceValue === undefined) { return; } // Handle Sets if (targetValue instanceof Set && sourceValue instanceof Set) { // eslint-disable-next-line perfectionist/sort-sets target[key] = finalOptions.set === 'merge' ? new Set([...targetValue, ...sourceValue]) : sourceValue; return; } // Handle Arrays if (Array.isArray(targetValue) && Array.isArray(sourceValue)) { if (finalOptions.array === 'replace') { target[key] = sourceValue; } else if (finalOptions.array === 'append') { target[key] = targetValue.concat(sourceValue); } else { // 'merge' sourceValue.forEach((item, index) => { if (isPlainObject(targetValue[index]) && isPlainObject(item)) { baseMerge(targetValue[index], item); } else if (index < targetValue.length) { targetValue[index] = item; } else { targetValue.push(item); } }); } return; } // Handle plain objects if (isPlainObject(targetValue) && isPlainObject(sourceValue)) { baseMerge(targetValue, sourceValue); return; } // For all other cases (primitives, non-plain objects, etc.), replace the value. target[key] = sourceValue; }); }; for (const source of sources) { if (source) { baseMerge(output, source); } } return output; } //# sourceMappingURL=merge.js.map