All files / src/utils merge.ts

92% Statements 23/25
86.36% Branches 19/22
100% Functions 3/3
91.3% Lines 21/23

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52    4x 56x               4x 36x     19x 19x   17x   17x 17x 20x 20x   20x 20x   20x     3x     17x 5x   5x 5x         12x           17x    
import { DeepPartial } from '../types'
 
const isObject = (item: any): item is Record<string, any> => {
    return item && typeof item === 'object' && !Array.isArray(item)
}
 
/**
 * Deep merges two objects.
 * properties in source override properties in target.
 * Arrays are replaced, not concatenated (usually preferred for config).
 */
export const deepMerge = <T extends object>(target: T, ...sources: DeepPartial<T>[]): T => {
    if (!sources.length) return target
 
    // Merge the first source into target
    const source = sources.shift()
    if (!source) return deepMerge(target, ...sources)
 
    const output = { ...target }
 
    Eif (isObject(target) && isObject(source)) {
        Object.keys(source).forEach((key) => {
            const sourceKey = key as keyof typeof source
            const targetKey = key as keyof typeof target
 
            const sourceValue = source[sourceKey]
            const targetValue = target[targetKey]
 
            if (sourceValue === '' || sourceValue === null || sourceValue === undefined) {
                // Skip empty strings, nulls, and undefined to support inheritance
                // (source empty means fall back to target)
                return
            }
 
            if (isObject(sourceValue)) {
                Iif (!(sourceKey in target)) {
                    Object.assign(output, { [key]: sourceValue })
                } else if (isObject(targetValue)) {
                    ;(output as any)[key] = deepMerge(targetValue as object, sourceValue as any)
                } else E{
                    Object.assign(output, { [key]: sourceValue })
                }
            } else {
                Object.assign(output, { [key]: sourceValue })
            }
        })
    }
 
    // Recursively merge remaining sources
    return deepMerge(output, ...sources)
}