p5.party
Version:
Pre-release! An easy to use library for simple multi-user sketches with p5.js.
106 lines (94 loc) • 3.09 kB
text/typescript
import { JSONObject, JSONValue } from "./validate";
import * as log from "./log";
/**
* patches _old to deep equal _new
* needed so objects can be updated without breaking references to them
*
* @param oldObject object to be updated
* @param newObject object with values to use for update
* @param path path to the object to be updated
*/
export function patchInPlace(
oldObject: JSONObject,
newObject: JSONObject,
path = ""
): void {
if (typeof oldObject !== "object") {
log.error("_old is not an object");
return;
}
if (typeof newObject !== "object") {
log.error("_new is not an object");
return;
}
const oldKeys = Object.keys(oldObject).reverse();
const newKeys = Object.keys(newObject);
// remove oldObject keys that are not in newObject
for (const key of oldKeys) {
if (!Object.prototype.hasOwnProperty.call(newObject, key)) {
// log.debug(`remove ${_keyPath}.${key}`);
if (Array.isArray(oldObject)) {
oldObject.splice(parseInt(key), 1);
} else {
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete oldObject[key];
}
}
}
// add newObject keys that are not in oldObject
for (const key of newKeys) {
if (!Object.prototype.hasOwnProperty.call(oldObject, key)) {
// log.debug(`add ${_keyPath}.${key}`);
oldObject[key] = newObject[key];
}
}
// patch shared object and array keys
for (const key of newKeys) {
if (Object.prototype.hasOwnProperty.call(oldObject, key)) {
const oldType = getMergeType(oldObject[key]);
const newType = getMergeType(newObject[key]);
// bail if type is unsupported
if (newType === "unsupported") {
log.error(
`${path}.${key} is unsupported type: ${typeof newObject[key]}`
);
continue;
}
// merge objects
if (oldType === "object" && newType === "object") {
// casts are safe; objects in JSONObjects are always JSON Objects
patchInPlace(
oldObject[key] as JSONObject,
newObject[key] as JSONObject,
`${path}.${key}`
);
continue;
}
// merge arrays
if (oldType === "array" && newType === "array") {
// casts are safe; arrays in JSONObjects can be treated by
// this function as JSONObjects
patchInPlace(
oldObject[key] as JSONObject,
newObject[key] as JSONObject,
`${path}.${key}`
);
continue;
}
// replace everything else
if (oldObject[key] !== newObject[key]) {
oldObject[key] = newObject[key];
}
}
}
}
// module.exports = { patchInPlace };
function getMergeType(value: JSONValue): string {
if (value === null) return "null";
if (Array.isArray(value)) return "array";
if (typeof value === "object") return "object";
if (typeof value === "boolean") return "primitive";
if (typeof value === "number" && Number.isFinite(value)) return "primitive";
if (typeof value === "string") return "primitive";
return "unsupported";
}