site-panzai/node_modules/microdiff/dist/index.js
2026-11-13 13:57:04 +01:00

69 lines
2.5 KiB
JavaScript

const richTypes = ["Date", "RegExp", "String", "Number"];
const temporalTypes = Object.getOwnPropertyNames(globalThis.Temporal || {});
export default function diff(obj, newObj, options = { cyclesFix: true }, _stack = []) {
let diffs = [];
const isObjArray = Array.isArray(obj);
for (const key in obj) {
const value = obj[key];
const path = isObjArray ? +key : key;
if (!(key in newObj)) {
diffs.push({
type: "REMOVE",
path: [path],
oldValue: value,
});
continue;
}
const newValue = newObj[key];
const areCompatibleObjects = typeof value === "object" &&
typeof newValue === "object" &&
Array.isArray(value) === Array.isArray(newValue);
// Only compute for non-null objects — primitives and null skip this
// entirely since Object.getPrototypeOf is expensive to call on every key
const objConstructor = areCompatibleObjects && value
? Object.getPrototypeOf(value)?.constructor?.name
: undefined;
if (value &&
newValue &&
areCompatibleObjects &&
!richTypes.includes(objConstructor) &&
!temporalTypes.includes(objConstructor) &&
(!options.cyclesFix || !_stack.includes(value))) {
// Recurse into objects and arrays
if (options.cyclesFix) {
_stack.push(value);
}
const subDiffs = diff(value, newValue, options, _stack);
if (options.cyclesFix) {
_stack.pop();
}
for (const subDiff of subDiffs) {
subDiff.path.unshift(path);
diffs.push(subDiff);
}
}
else if (!(Object.is(value, newValue) ||
(temporalTypes.includes(objConstructor) &&
String(value) === String(newValue)) ||
(richTypes.includes(objConstructor) &&
(isNaN(value) ? value + "" === newValue + "" : +value === +newValue)))) {
diffs.push({
path: [path],
type: "CHANGE",
value: newValue,
oldValue: value,
});
}
}
const isNewObjArray = Array.isArray(newObj);
for (const key in newObj) {
if (!(key in obj)) {
diffs.push({
type: "CREATE",
path: [isNewObjArray ? +key : key],
value: newObj[key],
});
}
}
return diffs;
}