deepmerge-plus
Version:
A library for deep (recursive) merging of Javascript objects
199 lines (168 loc) • 4.86 kB
text/typescript
import isMergeableObject from 'is-mergeable-object';
function emptyTarget(val)
{
return Array.isArray(val) ? [] : {}
}
function cloneUnlessOtherwiseSpecified(value, optionsArgument: IOptions, tmp?: ICache)
{
const clone = !optionsArgument || optionsArgument.clone !== false;
const bool = clone && _isMergeableObject(value, optionsArgument, tmp);
let ret = (bool)
? deepmerge(emptyTarget(value), value, optionsArgument)
: value;
if (optionsArgument?.keyValueOrMode && !bool && tmp && ('key' in tmp))
{
if (tmp.destination)
{
//console.log('destination', tmp.destination[tmp.key], ret, tmp.key);
ret = tmp.destination[tmp.key] || ret;
}
if (tmp.target)
{
//console.log('target', tmp.target[tmp.key], ret, tmp.key);
ret = tmp.target[tmp.key] || ret;
}
if (tmp.source)
{
//console.log('source', tmp.source[tmp.key], ret, tmp.key);
ret = tmp.source[tmp.key] || ret;
}
}
return ret;
}
export function _isMergeableObject(value, optionsArgument: IOptions, tmp?: ICache): boolean
{
let ret = optionsArgument?.isMergeableObject?.(value, isMergeableObject, optionsArgument, tmp) as any;
if (ret === null || typeof ret === 'undefined')
{
if ((typeof value?.[SYMBOL_IS_MERGEABLE] == 'boolean'))
{
ret = value[SYMBOL_IS_MERGEABLE];
}
else
{
ret = isMergeableObject(value);
}
}
return ret
}
function defaultArrayMerge(target, source, optionsArgument: IOptions)
{
return target.concat(source).map(function (element, index, array)
{
return cloneUnlessOtherwiseSpecified(element, optionsArgument, {
key: index,
})
})
}
function mergeObject(target, source, optionsArgument: IOptions)
{
let destination = {};
if (_isMergeableObject(target, optionsArgument))
{
Object.keys(target).forEach(function (key)
{
destination[key] = cloneUnlessOtherwiseSpecified(target[key], optionsArgument, {
key,
source,
target,
destination,
})
})
}
Object.keys(source).forEach(function (key)
{
if (!_isMergeableObject(source[key], optionsArgument, {
key,
source,
target,
}) || !target[key])
{
destination[key] = cloneUnlessOtherwiseSpecified(source[key], optionsArgument, {
key,
source,
target,
})
}
else
{
destination[key] = deepmerge(target[key], source[key], optionsArgument)
}
});
return destination
}
export function deepmerge<T1, T2>(x: T1, y: T2, options?: IOptions): Partial<T1 & T2>
export function deepmerge<T>(x: Partial<T>, y: Partial<T>, options?: IOptions): Partial<T>
export function deepmerge(target, source, optionsArgument)
{
const sourceIsArray = Array.isArray(source);
const targetIsArray = Array.isArray(target);
const options = optionsArgument || { arrayMerge: defaultArrayMerge };
const sourceAndTargetTypesMatch = sourceIsArray === targetIsArray;
if (!sourceAndTargetTypesMatch)
{
return cloneUnlessOtherwiseSpecified(source, optionsArgument, {
target,
source,
});
}
else if (sourceIsArray)
{
let arrayMerge = options.arrayMerge || defaultArrayMerge;
return arrayMerge(target, source, optionsArgument);
}
else
{
return mergeObject(target, source, optionsArgument);
}
}
export interface ICache
{
key?
source?
target?
destination?
}
export interface IOptions
{
clone?: boolean;
arrayMerge?(destination: any[], source: any[], options?: IOptions): any[];
isMergeableObject?(value, isMergeableObject: typeof isMergeable, optionsArgument?: IOptions, key?): void;
isMergeableObject?(value, isMergeableObject: typeof isMergeable, optionsArgument?: IOptions, key?): boolean;
/**
* (val = old || new) mode
*/
keyValueOrMode?: boolean,
}
export function isMergeable(value: any): boolean
{
return isMergeableObject(value)
}
const SYMBOL_IS_MERGEABLE = Symbol.for('SYMBOL_IS_MERGEABLE');
export { SYMBOL_IS_MERGEABLE }
export function deepmergeAll<T, T2 = any>(array: Array<Partial<T2 & T>>, optionsArgument?: IOptions): T2 & T
{
if (!Array.isArray(array))
{
throw new Error('first argument should be an array')
}
// @ts-ignore
return array.reduce(function (prev, next)
{
return deepmerge(prev, next, optionsArgument)
}, {})
}
export { deepmergeAll as all }
export default deepmerge
// @ts-ignore
if (process.env.TSDX_FORMAT !== 'esm')
{
Object.defineProperty(deepmerge, "__esModule", { value: true });
Object.defineProperty(deepmerge, 'deepmerge', { value: deepmerge });
Object.defineProperty(deepmerge, 'default', { value: deepmerge });
Object.defineProperty(deepmerge, 'isMergeable', { value: isMergeable });
Object.defineProperty(deepmerge, 'SYMBOL_IS_MERGEABLE', { value: SYMBOL_IS_MERGEABLE });
Object.defineProperty(deepmerge, 'deepmergeAll', { value: deepmergeAll });
Object.defineProperty(deepmerge, 'all', { value: deepmergeAll });
Object.defineProperty(deepmerge, '_isMergeableObject', { value: _isMergeableObject });
}