Files
inpx-web/client/share/diffUtils.js

143 lines
3.6 KiB
JavaScript

const _ = require('lodash');
function getObjDiff(oldObj, newObj, opts = {}) {
const {
exclude = [],
excludeAdd = [],
excludeDel = [],
} = opts;
const ex = new Set(exclude);
const exAdd = new Set(excludeAdd);
const exDel = new Set(excludeDel);
const makeObjDiff = (oldObj, newObj, keyPath) => {
const result = {__isDiff: true, change: {}, add: {}, del: []};
keyPath = `${keyPath}${keyPath ? '/' : ''}`;
for (const key of Object.keys(oldObj)) {
const kp = `${keyPath}${key}`;
if (newObj.hasOwnProperty(key)) {
if (ex.has(kp))
continue;
if (!_.isEqual(oldObj[key], newObj[key])) {
if (_.isObject(oldObj[key]) && _.isObject(newObj[key])) {
result.change[key] = makeObjDiff(oldObj[key], newObj[key], kp);
} else {
result.change[key] = _.cloneDeep(newObj[key]);
}
}
} else {
if (exDel.has(kp))
continue;
result.del.push(key);
}
}
for (const key of Object.keys(newObj)) {
const kp = `${keyPath}${key}`;
if (exAdd.has(kp))
continue;
if (!oldObj.hasOwnProperty(key)) {
result.add[key] = _.cloneDeep(newObj[key]);
}
}
return result;
}
return makeObjDiff(oldObj, newObj, '');
}
function isObjDiff(diff) {
return (_.isObject(diff) && diff.__isDiff && diff.change && diff.add && diff.del);
}
function isEmptyObjDiff(diff) {
return (!isObjDiff(diff) ||
!(Object.keys(diff.change).length ||
Object.keys(diff.add).length ||
diff.del.length
)
);
}
function isEmptyObjDiffDeep(diff, opts = {}) {
if (!isObjDiff(diff))
return true;
const {
isApplyChange = true,
isApplyAdd = true,
isApplyDel = true,
} = opts;
let notEmptyDeep = false;
const change = diff.change;
for (const key of Object.keys(change)) {
if (_.isObject(change[key]))
notEmptyDeep |= !isEmptyObjDiffDeep(change[key], opts);
else if (isApplyChange)
notEmptyDeep = true;
}
return !(
notEmptyDeep ||
(isApplyAdd && Object.keys(diff.add).length) ||
(isApplyDel && diff.del.length)
);
}
function applyObjDiff(obj, diff, opts = {}) {
const {
isAddChanged = false,
isApplyChange = true,
isApplyAdd = true,
isApplyDel = true,
} = opts;
let result = _.cloneDeep(obj);
if (!diff.__isDiff)
return result;
const change = diff.change;
for (const key of Object.keys(change)) {
if (result.hasOwnProperty(key)) {
if (_.isObject(change[key])) {
result[key] = applyObjDiff(result[key], change[key], opts);
} else {
if (isApplyChange)
result[key] = _.cloneDeep(change[key]);
}
} else if (isAddChanged) {
result[key] = _.cloneDeep(change[key]);
}
}
if (isApplyAdd) {
for (const key of Object.keys(diff.add)) {
result[key] = _.cloneDeep(diff.add[key]);
}
}
if (isApplyDel && diff.del.length) {
for (const key of diff.del) {
delete result[key];
}
if (_.isArray(result))
result = result.filter(v => v);
}
return result;
}
module.exports = {
getObjDiff,
isObjDiff,
isEmptyObjDiff,
applyObjDiff,
}