UNPKG

@ou-imdt/utils

Version:

Utility library for interactive media development

53 lines (45 loc) 2.1 kB
export const skip = Symbol('skip merge value'); export function isMergeable(target) { return typeof target === 'object' && target !== null; } /** * recursively merges enumerable own properties (string/symbol keyed) from one or more source objects to a target object. * - doesn't throw on unsupported source types (non-object) * - creates nested objects on target where needed * - doesn't override defined target values with undefined source values * @param {object} target - the target object * @param {object|function} params - source object(s), optional callback function * @returns {object} - the modified target object */ export default function mergeObject(target, ...params) { if (typeof target !== 'object' || target === null) return target; const depth = (typeof params[params.length - 1] === 'number') ? params.pop() : 0; const stack = (depth === 0) ? target : params.pop(); const callback = (typeof params[params.length - 1] === 'function') ? params.pop() : null; const [source, ...sourceList] = params.filter(value => value !== null); if (typeof source === 'object' && source !== null) { const keys = Reflect.ownKeys(source).filter(key => Object.prototype.propertyIsEnumerable.call(source, key)); keys.forEach((key) => { const targetValue = target[key]; const sourceValue = source[key]; if (typeof targetValue !== 'undefined' && typeof sourceValue === 'undefined') return; const customValue = callback?.({ sourceValue, targetValue, key, target, source, stack, depth }); if (typeof customValue !== 'undefined') { if (customValue !== skip) { target[key] = customValue; } return; } if (isMergeable(sourceValue)) { if (!isMergeable(target[key])) { target[key] = Array.isArray(sourceValue) ? [] : {}; } target[key] = mergeObject(target[key], sourceValue, callback, stack, depth + 1); } else { target[key] = sourceValue; } }); } if (sourceList.length) mergeObject(target, ...sourceList, callback); return target; }