UNPKG

datum-merge

Version:

Simplified diff and merging for deeply nested objects

150 lines (149 loc) 5.81 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.diffToPatchLog = diffToPatchLog; exports.deepPatchLog = deepPatchLog; exports.applyPatchLog = applyPatchLog; exports.revertPatchLog = revertPatchLog; exports.forcePatchLog = forcePatchLog; exports.immutablePatch = immutablePatch; exports.asLodashPath = asLodashPath; exports.getPointerValue = getPointerValue; const lodash_es_1 = require("lodash-es"); const type_utils_1 = require("./type-utils"); const datum_utils_1 = require("./datum-utils"); const diff_high_1 = require("./diff-high"); function diffToPatchLog(differences, storePrev = false) { var _a, _b, _c; const jsonPatch = []; for (const dif of differences) { let pointer = asJsonPointer((_a = dif.path) !== null && _a !== void 0 ? _a : []); switch (dif.kind) { case "N": jsonPatch.push({ op: "add", path: pointer, value: dif.rhs }); break; case "E": jsonPatch.push({ op: "replace", path: pointer, value: dif.rhs, prev: dif.lhs }); break; case "D": jsonPatch.push({ op: "remove", path: pointer, prev: dif.lhs }); break; case "A": pointer = `${pointer}/${dif.index}`; if (((_b = dif.item) === null || _b === void 0 ? void 0 : _b.kind) === 'N') { jsonPatch.push({ op: "add", path: pointer, value: dif.item.rhs }); } if (((_c = dif.item) === null || _c === void 0 ? void 0 : _c.kind) === 'D') { jsonPatch.push({ op: "remove", path: pointer, prev: dif.item.lhs }); } break; } } if (!storePrev) { jsonPatch.forEach((p) => delete p.prev); } return jsonPatch; } function deepPatchLog(lhsObj, rhsObj, orderInd = false, storePrev = false) { const differences = (0, diff_high_1.deepDiffLow)(lhsObj, rhsObj, orderInd); return !differences ? [] : diffToPatchLog(differences, storePrev); } ; function applyPatchLog(patchLog, target) { if (!target || !(patchLog === null || patchLog === void 0 ? void 0 : patchLog.length)) return false; let changed = false; for (const patchItem of patchLog) { const difPath = asLodashPath(patchItem.path); if (patchItem.op === "test") continue; if (patchItem.op === "remove") { changed = (0, lodash_es_1.unset)(target, difPath) || changed; continue; } const targetVal = (0, lodash_es_1.get)(target, difPath); const sourceVal = patchItem.value; if ((0, type_utils_1.isNullish)(targetVal) && (0, type_utils_1.isNullish)(sourceVal)) continue; if ((0, type_utils_1.isNullish)(targetVal) || !(0, datum_utils_1.deepEquals)(targetVal, sourceVal)) { (0, lodash_es_1.set)(target, difPath, (0, datum_utils_1.deepClone)(sourceVal)); changed = true; } } return changed; } function revertPatchLog(patchLog, target) { if (!target || !(patchLog === null || patchLog === void 0 ? void 0 : patchLog.length)) return false; let changed = false; for (const patchItem of patchLog) { const difPath = asLodashPath(patchItem.path); if (patchItem.op === "test") continue; if (patchItem.op === "add") { changed = (0, lodash_es_1.unset)(target, difPath) || changed; continue; } (0, lodash_es_1.set)(target, difPath, patchItem.prev); changed = true; } return changed; } function forcePatchLog(patchLog, target = {}) { for (const patchItem of patchLog) { const difPath = patchItem.path.startsWith("/") ? asLodashPath(patchItem.path) : (0, lodash_es_1.toPath)(patchItem.path); if (!(difPath === null || difPath === void 0 ? void 0 : difPath.length)) continue; const value = patchItem.value; if ((0, type_utils_1.isNullish)(value)) { (0, lodash_es_1.unset)(target, difPath); } if ((0, type_utils_1.isPrimitive)(value)) { (0, lodash_es_1.set)(target, difPath, value); } else { (0, lodash_es_1.set)(target, difPath, (0, datum_utils_1.deepClone)(value)); } } } function immutablePatch(target, patchSrc, patchDir) { const targetCopy = (0, datum_utils_1.deepClone)(target !== null && target !== void 0 ? target : {}); if (patchDir === "revert") { revertPatchLog(patchSrc, targetCopy); } else if (patchDir === "force") { forcePatchLog(patchSrc, targetCopy); } else { applyPatchLog(patchSrc, targetCopy); } return targetCopy; } ; function escapePathPart(path) { if (typeof path === 'number') return path.toString(); if (typeof path === 'symbol') return path.toString(); if (path.indexOf('/') === -1 && path.indexOf('~') === -1) return path; return path.replace(/~/g, '~0').replace(/\//g, '~1'); } function unescapePathPart(path) { return path.replace(/~1/g, '/').replace(/~0/g, '~'); } function asJsonPointer(path) { return !(path === null || path === void 0 ? void 0 : path.length) ? "" : "/" + path.map((s) => escapePathPart(s)).join("/"); } function asLodashPath(pointer) { if (!pointer || !pointer.startsWith("/")) return []; const parts = pointer.slice(1).split("/") .map((s) => unescapePathPart(s)); return !(parts === null || parts === void 0 ? void 0 : parts.length) ? [] : (0, lodash_es_1.toPath)(parts.join(".")); } function getPointerValue(document, pointer) { return pointer === "" ? document : (0, lodash_es_1.get)(document, asLodashPath(pointer)); }