datum-merge
Version:
Simplified diff and merging for deeply nested objects
150 lines (149 loc) • 5.81 kB
JavaScript
;
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));
}