UNPKG

json-joy

Version:

Collection of libraries for building collaborative editing apps.

121 lines (120 loc) 4.27 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.JsonPatchDiff = void 0; const tslib_1 = require("tslib"); const deepEqual_1 = require("@jsonjoy.com/util/lib/json-equal/deepEqual"); const str = tslib_1.__importStar(require("../util/diff/str")); const line = tslib_1.__importStar(require("../util/diff/line")); const json_hash_1 = require("../json-hash"); class JsonPatchDiff { constructor() { this.patch = []; } diffVal(path, src, dst) { if ((0, deepEqual_1.deepEqual)(src, dst)) return; this.patch.push({ op: 'replace', path, value: dst }); } diffStr(path, src, dst) { if (src === dst) return; const patch = this.patch; str.apply(str.diff(src, dst), src.length, (pos, str) => patch.push({ op: 'str_ins', path, pos, str }), (pos, len, str) => patch.push({ op: 'str_del', path, pos, len, str })); } diffBin(path, src, dst) { throw new Error('Not implemented'); } diffObj(path, src, dst) { const patch = this.patch; for (const key in src) { if (key in dst) { const val1 = src[key]; const val2 = dst[key]; if (val1 === val2) continue; this.diffAny(path + '/' + key, val1, val2); } else { patch.push({ op: 'remove', path: path + '/' + key }); } } for (const key in dst) { if (key in src) continue; patch.push({ op: 'add', path: path + '/' + key, value: dst[key] }); } } diffArr(path, src, dst) { const srcLines = []; const dstLines = []; const srcLen = src.length; const dstLen = dst.length; for (let i = 0; i < srcLen; i++) srcLines.push((0, json_hash_1.structHash)(src[i])); for (let i = 0; i < dstLen; i++) dstLines.push((0, json_hash_1.structHash)(dst[i])); const pfx = path + '/'; const patch = this.patch; const linePatch = line.diff(srcLines, dstLines); const length = linePatch.length; for (let i = length - 1; i >= 0; i--) { const [type, srcIdx, dstIdx] = linePatch[i]; switch (type) { case 0 /* line.LINE_PATCH_OP_TYPE.EQL */: break; case 2 /* line.LINE_PATCH_OP_TYPE.MIX */: { const srcValue = src[srcIdx]; const dstValue = dst[dstIdx]; this.diff(pfx + srcIdx, srcValue, dstValue); break; } case 1 /* line.LINE_PATCH_OP_TYPE.INS */: patch.push({ op: 'add', path: pfx + (srcIdx + 1), value: dst[dstIdx] }); break; case -1 /* line.LINE_PATCH_OP_TYPE.DEL */: patch.push({ op: 'remove', path: pfx + srcIdx }); break; } } } diffAny(path, src, dst) { switch (typeof src) { case 'string': { if (typeof dst === 'string') this.diffStr(path, src, dst); else this.diffVal(path, src, dst); break; } case 'number': case 'boolean': case 'bigint': { this.diffVal(path, src, dst); break; } case 'object': { if (!src || !dst || typeof dst !== 'object') { this.diffVal(path, src, dst); return; } if (Array.isArray(src)) { if (Array.isArray(dst)) this.diffArr(path, src, dst); else this.diffVal(path, src, dst); return; } this.diffObj(path, src, dst); break; } default: this.diffVal(path, src, dst); break; } } diff(path, src, dst) { this.diffAny(path, src, dst); return this.patch; } } exports.JsonPatchDiff = JsonPatchDiff;