UNPKG

datum-merge

Version:

Simplified diff and merging for deeply nested objects

139 lines (138 loc) 4.28 kB
import { concat, differenceWith, intersectionWith, unionWith, isEqual } from "lodash-es"; import { emptyValue, isArrayOfAny, isNullish, isPrimitive, typeOfValue } from "./type-utils"; import { areArraysEqual, deepClone } from "./datum-utils"; export const UpdateCode = { T: "T", C: "C", N: "N", Y: "Y", 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 = target.hasOwnProperty(label); const targetHas = targetKey && !isNullish(target[label]); if (!targetHas && !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.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 = target.hasOwnProperty(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; } 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 = unionWith(target[label], sourceVals, isEqual); break; case UpdateCode.XD: targetVals = differenceWith(target[label], sourceVals, isEqual); break; case UpdateCode.XI: targetVals = intersectionWith(target[label], sourceVals, isEqual); 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; } ;