UNPKG

datum-merge

Version:

Simplified diff and merging for deeply nested objects

180 lines (179 loc) 6.06 kB
import { concat, difference, differenceWith, intersection, intersectionWith, union, unionWith } from "lodash-es"; import { emptyValue, isArrayOf, isArrayOfAny, isNullish, isPrimitive, typeOfValue } from "./type-utils"; import { areArraysEqual, deepClone, deepEquals } from "./datum-utils"; export const UpdateCode = { T: "T", C: "C", N: "N", Y: "Y", F: "F", B: "B", U: "U", H: "H", I: "I", D: "D", XR: "XR", XM: "XM", XD: "XD", XI: "XI", XS: "XS", XF: "XF", }; export function mergeScalarField(target, source, label, mergeCode) { const sourceHas = !isNullish(source[label]); const targetKey = Object.prototype.hasOwnProperty.call(target, label); const targetHas = targetKey && !isNullish(target[label]); if (!targetKey && !sourceHas) { return false; } if (targetHas && sourceHas) { if (typeOfValue(target[label]) !== typeOfValue(source[label])) { throw new TypeError("scalar type mismatch for " + label); } if (target[label] === source[label]) { return false; } } let migrateVal = false; switch (mergeCode) { case UpdateCode.N: return false; case UpdateCode.Y: target[label] = deepClone(source[label]); return true; case UpdateCode.F: migrateVal = true; break; case UpdateCode.B: if (sourceHas) migrateVal = true; break; case UpdateCode.U: if (targetHas) migrateVal = true; break; case UpdateCode.I: if (!targetHas) migrateVal = true; break; case UpdateCode.H: if (targetHas && sourceHas) migrateVal = true; break; case UpdateCode.D: if (targetHas && !sourceHas) migrateVal = true; break; case UpdateCode.XR: case UpdateCode.XS: case UpdateCode.XF: case UpdateCode.XM: case UpdateCode.XD: case UpdateCode.XI: return mergeVectorField(target, source, label, mergeCode); } if (!migrateVal) { return false; } target[label] = !sourceHas || isPrimitive(source[label]) ? source[label] : deepClone(source[label]); if (emptyValue(target[label])) { delete target[label]; return targetKey; } return true; } ; export function mergeVectorField(target, source, label, mergeCode) { var _a, _b; let sourceVals = source[label]; if (isNullish(sourceVals)) { return false; } let sourceVec = isArrayOfAny(sourceVals); const targetKey = Object.prototype.hasOwnProperty.call(target, label); const targetHas = targetKey && !isNullish(target[label]); const targetVec = isArrayOfAny(target[label]); if (targetHas && !targetVec) { throw new TypeError("type change to vector for " + label); } if (!sourceVec) { sourceVals = [sourceVals]; sourceVec = true; } const skipEquals = (!targetHas || isArrayOf(target[label], isPrimitive)) && isArrayOf(sourceVals, isPrimitive); let targetVals; switch (mergeCode) { case UpdateCode.N: return false; case UpdateCode.Y: target[label] = deepClone(sourceVals); return true; case UpdateCode.XR: targetVals = [...sourceVals]; break; case UpdateCode.XS: targetVals = concat((_a = target[label]) !== null && _a !== void 0 ? _a : [], sourceVals); break; case UpdateCode.XF: targetVals = concat(sourceVals, (_b = target[label]) !== null && _b !== void 0 ? _b : []); break; case UpdateCode.B: case UpdateCode.XM: targetVals = skipEquals ? union(target[label], sourceVals) : unionWith(target[label], sourceVals, deepEquals); break; case UpdateCode.XD: targetVals = skipEquals ? difference(target[label], sourceVals) : differenceWith(target[label], sourceVals, deepEquals); break; case UpdateCode.XI: targetVals = skipEquals ? intersection(target[label], sourceVals) : intersectionWith(target[label], sourceVals, deepEquals); break; default: return false; } if (!(targetVals === null || targetVals === void 0 ? void 0 : targetVals.length)) { delete target[label]; return targetKey; } const changed = !areArraysEqual(targetVals, target[label]); target[label] = !changed ? targetVals : deepClone(targetVals); return changed; } ; export function mergeVectors(mergeCode, arr1, arr2, equalsCond) { if (!(arr1 === null || arr1 === void 0 ? void 0 : arr1.length) && !(arr2 === null || arr2 === void 0 ? void 0 : arr2.length)) return []; if (equalsCond === false) { equalsCond = undefined; } else if (typeof equalsCond !== "function") { equalsCond = isArrayOf(arr1, isPrimitive) && isArrayOf(arr2, isPrimitive) ? undefined : deepEquals; } if (equalsCond) { switch (mergeCode) { case UpdateCode.XM: return unionWith(arr1, arr2, equalsCond); case UpdateCode.XI: return intersectionWith(arr1, arr2, equalsCond); case UpdateCode.XD: return differenceWith(arr1, arr2, equalsCond); } } switch (mergeCode) { case UpdateCode.XM: return union(arr1, arr2); case UpdateCode.XI: return intersection(arr1, arr2); case UpdateCode.XD: return difference(arr1, arr2); case UpdateCode.XS: return concat(arr1 !== null && arr1 !== void 0 ? arr1 : [], arr2 !== null && arr2 !== void 0 ? arr2 : []); } throw new Error("invalid merge code " + mergeCode); }