UNPKG

shelving

Version:

Toolkit for using data in JavaScript.

67 lines (66 loc) 2 kB
import { isArray, withArrayItems } from "./array.js"; import { isObject } from "./object.js"; // Internal shared by shallow/deep merge. function _merge(left, right, recursor) { if (left === right) return right; if (isArray(right)) return isArray(left) ? mergeArray(left, right) : right; if (isObject(right)) return isObject(left) && !isArray(left) ? mergeObject(left, right, recursor) : right; return right; } /** * Exact merge two unknown values. * - Always returns `right`. */ export function exactMerge(left, right) { return right; } export function shallowMerge(left, right) { return _merge(left, right, exactMerge); } export function deepMerge(left, right) { return _merge(left, right, deepMerge); } export function mergeArray(left, right) { if (left === right) return right; if (!right.length) return left; if (!left.length) return right; return withArrayItems(left, ...right); } export function mergeObject(left, right, recursor = exactMerge) { if (left === right) return right; // If `right` has no keys then merge result will always be `left` (because there's nothing to merge). const rightEntries = Object.entries(right); if (!rightEntries.length) return left; const leftKeys = Object.keys(left); const merged = { ...left }; let changed = false; for (const [k, r] of rightEntries) { if (leftKeys.includes(k)) { if (r === undefined) { changed = true; // Literal `undefined` means "delete this prop" } else { const l = left[k]; const m = recursor(l, r); if (m !== l) { changed = true; merged[k] = m; } } } else { changed = true; if (r !== undefined) merged[k] = r; } } return changed ? merged : left; }