@gamestdio/state-listener
Version:
Deeply compare JavaScript objects and listen to changes.
79 lines (78 loc) • 2.54 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
function compare(tree1, tree2) {
var patches = [];
generate(tree1, tree2, patches, []);
return patches;
}
exports.compare = compare;
function concat(arr, value) {
var newArr = arr.slice();
newArr.push(value);
return newArr;
}
function objectKeys(obj) {
if (Array.isArray(obj)) {
var keys_1 = new Array(obj.length);
for (var k = 0; k < keys_1.length; k++) {
keys_1[k] = "" + k;
}
return keys_1;
}
if (Object.keys) {
return Object.keys(obj);
}
var keys = [];
for (var i in obj) {
if (obj.hasOwnProperty(i)) {
keys.push(i);
}
}
return keys;
}
;
// Dirty check if obj is different from mirror, generate patches and update mirror
function generate(mirror, obj, patches, path) {
var newKeys = objectKeys(obj);
var oldKeys = objectKeys(mirror);
var deleted = false;
for (var t = oldKeys.length - 1; t >= 0; t--) {
var key = oldKeys[t];
var oldVal = mirror[key];
if (obj.hasOwnProperty(key) && !(obj[key] === undefined && oldVal !== undefined && Array.isArray(obj) === false)) {
var newVal = obj[key];
if (typeof oldVal == "object" && oldVal != null && typeof newVal == "object" && newVal != null) {
generate(oldVal, newVal, patches, concat(path, key));
}
else {
if (oldVal !== newVal) {
patches.push({
operation: "replace",
path: concat(path, key),
value: newVal,
previousValue: oldVal
});
}
}
}
else {
patches.push({ operation: "remove", path: concat(path, key) });
deleted = true; // property has been deleted
}
}
if (!deleted && newKeys.length == oldKeys.length) {
return;
}
for (var t = newKeys.length - 1; t >= 0; t--) {
var key = newKeys[t];
if (!mirror.hasOwnProperty(key) && obj[key] !== undefined) {
var newVal = obj[key];
var addPath = concat(path, key);
// compare deeper additions
if (typeof newVal == "object" && newVal != null) {
generate({}, newVal, patches, addPath);
}
patches.push({ operation: "add", path: addPath, value: newVal });
}
}
}