@portabletext/patches
Version:
Portable Text Patches
221 lines (220 loc) • 7.25 kB
JavaScript
import isObject from "lodash/isObject.js";
import isString from "lodash/isString.js";
import findIndex from "lodash/findIndex.js";
import clone from "lodash/clone.js";
import omit from "lodash/omit.js";
import { applyPatches, parsePatch, makePatches, stringifyPatches } from "@sanity/diff-match-patch";
const BEFORE = "before", AFTER = "after";
function insert$1(array, position, index, ...args) {
if (position !== BEFORE && position !== AFTER)
throw new Error(
`Invalid position "${position}", must be either ${BEFORE} or ${AFTER}`
);
const items = flatten(...args);
if (array.length === 0)
return items;
const len = array.length, idx = Math.abs((len + index) % len) % len, normalizedIdx = position === "after" ? idx + 1 : idx, copy = array.slice();
return copy.splice(normalizedIdx, 0, ...flatten(items)), copy;
}
function flatten(...values) {
return values.reduce((prev, item) => prev.concat(item), []);
}
const hasOwn = Object.prototype.hasOwnProperty.call.bind(
Object.prototype.hasOwnProperty
);
function move(arr, from, to) {
const nextValue = arr.slice(), val = nextValue[from];
return nextValue.splice(from, 1), nextValue.splice(to, 0, val), nextValue;
}
function findTargetIndex(array, pathSegment) {
if (typeof pathSegment == "number")
return pathSegment;
const index = findIndex(array, pathSegment);
return index === -1 ? !1 : index;
}
function apply$3(value, patch) {
const nextValue = value.slice();
if (patch.path.length === 0) {
if (patch.type === "setIfMissing") {
if (!Array.isArray(patch.value))
throw new Error("Cannot set value of an array to a non-array");
return value === void 0 ? patch.value : value;
} else if (patch.type === "set") {
if (!Array.isArray(patch.value))
throw new Error("Cannot set value of an array to a non-array");
return patch.value;
} else {
if (patch.type === "unset")
return;
if (patch.type === "move") {
if (!patch.value || !hasOwn(patch.value, "from") || !hasOwn(patch.value, "to"))
throw new Error(
`Invalid value of 'move' patch. Expected a value with "from" and "to" indexes, instead got: ${JSON.stringify(
patch.value
)}`
);
return move(nextValue, patch.value.from, patch.value.to);
}
}
throw new Error(`Invalid array operation: ${patch.type}`);
}
const [head, ...tail] = patch.path, index = findTargetIndex(value, head);
if (index === !1)
return nextValue;
if (tail.length === 0) {
if (patch.type === "insert") {
const { position, items } = patch;
return insert$1(value, position, index, items);
} else if (patch.type === "unset") {
if (typeof index != "number")
throw new Error(
`Expected array index to be a number, instead got "${index}"`
);
return nextValue.splice(index, 1), nextValue;
}
}
return nextValue[index] = _apply(nextValue[index], {
...patch,
path: tail
}), nextValue;
}
function apply$2(value, patch) {
const nextValue = clone(value);
if (patch.path.length === 0) {
if (patch.type === "set") {
if (!isObject(patch.value))
throw new Error("Cannot set value of an object to a non-object");
return patch.value;
} else {
if (patch.type === "unset")
return;
if (patch.type === "setIfMissing")
return value === void 0 ? patch.value : value;
}
throw new Error(`Invalid object operation: ${patch.type}`);
}
const [head, ...tail] = patch.path;
if (typeof head != "string")
throw new Error(`Expected field name to be a string, instad got: ${head}`);
return tail.length === 0 && patch.type === "unset" ? omit(nextValue, head) : (nextValue[head] = _apply(nextValue[head], {
...patch,
path: tail
}), nextValue);
}
const OPERATIONS$1 = {
replace(_currentValue, nextValue) {
return nextValue;
},
set(_currentValue, nextValue) {
return nextValue;
},
setIfMissing(currentValue, nextValue) {
return currentValue === void 0 ? nextValue : currentValue;
},
unset(_currentValue, _nextValue) {
},
inc(currentValue, nextValue) {
return currentValue + nextValue;
},
dec(currentValue, nextValue) {
return currentValue - nextValue;
}
}, SUPPORTED_PATCH_TYPES$1 = Object.keys(OPERATIONS$1);
function apply$1(value, patch) {
if (!SUPPORTED_PATCH_TYPES$1.includes(patch.type))
throw new Error(
`Received patch of unsupported type: "${JSON.stringify(
patch.type
)}" for primitives. This is most likely a bug.`
);
if (patch.path.length > 0)
throw new Error(
`Cannot apply deep operations on primitive values. Received patch with type "${patch.type}" and path "${patch.path.map((path) => JSON.stringify(path)).join(".")} that targeted the value "${JSON.stringify(value)}"`
);
return OPERATIONS$1[patch.type](value, patch.value);
}
const OPERATIONS = {
replace(_currentValue, nextValue) {
return nextValue;
},
set(_currentValue, nextValue) {
return nextValue;
},
setIfMissing(currentValue, nextValue) {
return currentValue === void 0 ? nextValue : currentValue;
},
unset(_currentValue, _nextValue) {
},
diffMatchPatch(currentValue, nextValue) {
const [result] = applyPatches(parsePatch(nextValue), currentValue, {
allowExceedingIndices: !0
});
return result;
}
}, SUPPORTED_PATCH_TYPES = Object.keys(OPERATIONS);
function apply(value, patch) {
if (!SUPPORTED_PATCH_TYPES.includes(patch.type))
throw new Error(
`Received patch of unsupported type: "${JSON.stringify(
patch.type
)}" for string. This is most likely a bug.`
);
if (patch.path.length > 0)
throw new Error(
`Cannot apply deep operations on string values. Received patch with type "${patch.type}" and path "${patch.path.join(".")} that targeted the value "${JSON.stringify(value)}"`
);
const func = OPERATIONS[patch.type];
if (func)
return func(value, patch.value);
throw new Error("Unknown patch type");
}
function applyAll(value, patches) {
return patches.reduce(_apply, value);
}
function applyPatch(value, patch) {
return Array.isArray(value) ? apply$3(value, patch) : isString(value) ? apply(value, patch) : isObject(value) ? apply$2(value, patch) : apply$1(value, patch);
}
function _apply(value, patch) {
return applyPatch(value, patch);
}
function setIfMissing(value, path = []) {
return {
type: "setIfMissing",
path,
value
};
}
function diffMatchPatch(currentValue, nextValue, path = []) {
const patches = makePatches(currentValue, nextValue), patch = stringifyPatches(patches);
return { type: "diffMatchPatch", path, value: patch };
}
function insert(items, position, path = []) {
return {
type: "insert",
path,
position,
items
};
}
function set(value, path = []) {
return { type: "set", path, value };
}
function unset(path = []) {
return { type: "unset", path };
}
function prefixPath(patch, segment) {
return {
...patch,
path: [segment, ...patch.path]
};
}
export {
applyAll,
diffMatchPatch,
insert,
prefixPath,
set,
setIfMissing,
unset
};
//# sourceMappingURL=index.js.map