datum-merge
Version:
Simplified diff and merging for deeply nested objects
165 lines (164 loc) • 7.31 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.fillUpdateCodes = void 0;
exports.detailMerge = detailMerge;
exports.immutableDetailMerge = immutableDetailMerge;
exports.getDetailKeys = getDetailKeys;
exports.customMerge = customMerge;
exports.bypassMerge = bypassMerge;
exports.immutableCustomMerge = immutableCustomMerge;
const type_utils_1 = require("./type-utils");
const datum_utils_1 = require("./datum-utils");
const diff_high_1 = require("./diff-high");
const merge_low_1 = require("./merge-low");
const merge_high_1 = require("./merge-high");
function detailMerge(target, source, mergeCodes) {
if (!(0, type_utils_1.isObject)(mergeCodes) || (0, type_utils_1.emptyObject)(mergeCodes)) {
return false;
}
let changed = false;
const mergeKeys = (0, datum_utils_1.getObjectKeys)(mergeCodes);
for (const label of mergeKeys) {
const mergeCode = mergeCodes[label];
if (!(0, type_utils_1.isString)(mergeCode)) {
if ((0, type_utils_1.isObject)(target[label]) && (0, type_utils_1.isObject)(source[label])) {
changed = detailMerge(target[label], source[label], mergeCode) || changed;
continue;
}
if (!(0, type_utils_1.isObject)(target[label]) && (0, type_utils_1.isObject)(source[label])) {
const targetVal = {};
detailMerge(targetVal, source[label], mergeCode);
changed = (0, merge_low_1.mergeScalarField)(target, { [label]: targetVal }, label, merge_low_1.UpdateCode.I) || changed;
continue;
}
changed = (0, merge_low_1.mergeScalarField)(target, source, label, merge_low_1.UpdateCode.I) || changed;
continue;
}
if (mergeCode.toString().startsWith("X")
? (0, merge_low_1.mergeVectorField)(target, source, label, mergeCode)
: (0, merge_low_1.mergeScalarField)(target, source, label, mergeCode)) {
changed = true;
}
}
return changed;
}
function immutableDetailMerge(target, source, mergeCodes) {
const targetCopy = (0, datum_utils_1.deepClone)(target);
detailMerge(targetCopy, source, mergeCodes);
return targetCopy;
}
function getDetailKeys(obj, mergeCodes, excludeKeys) {
const includeKeys = [];
if (!obj || !mergeCodes) {
return includeKeys;
}
for (const label of (0, datum_utils_1.getObjectKeys)(obj, excludeKeys)) {
const labelConf = mergeCodes[label];
if ((0, type_utils_1.isString)(labelConf) && (0, merge_high_1.updateCodeInfo)(labelConf).enable)
includeKeys.push(label);
if ((0, type_utils_1.isObject)(labelConf) && !(0, type_utils_1.emptyObject)(labelConf))
includeKeys.push(label);
}
return includeKeys;
}
function customMerge(target, source, mergeConf, excludeKeys) {
switch (mergeConf) {
case merge_low_1.UpdateCode.C:
return false;
case merge_low_1.UpdateCode.T:
return {};
case merge_low_1.UpdateCode.N:
return false;
case merge_low_1.UpdateCode.Y:
return bypassMerge(target, source);
}
const mergeCodes = (0, exports.fillUpdateCodes)(source, mergeConf, false, excludeKeys);
if ((0, type_utils_1.emptyObject)(mergeCodes)) {
return false;
}
const targetBkp = (0, datum_utils_1.deepClone)(target);
const changed = detailMerge(target, source, mergeCodes);
const delta = (0, diff_high_1.deepDiffTyped)(targetBkp, target, true);
if (changed || !(0, type_utils_1.emptyObject)(delta)) {
return delta;
}
return false;
}
function bypassMerge(target, source) {
if ((0, type_utils_1.emptyObject)(source)) {
return false;
}
let delta = (0, diff_high_1.deepDiffTyped)(target, source, true);
Object.assign(target, Object.assign({}, source));
delta = (0, datum_utils_1.selectObjKeys)(delta, (0, datum_utils_1.getObjectKeys)(source));
return (0, type_utils_1.emptyObject)(delta) ? false : delta;
}
function immutableCustomMerge(target, source, mergeConf, skipFill = false) {
const mergeCodes = !skipFill
? (0, exports.fillUpdateCodes)(source, mergeConf)
: mergeConf;
const targetCopy = (0, datum_utils_1.deepClone)(target);
detailMerge(targetCopy, source, mergeCodes);
return targetCopy;
}
const fillUpdateCodes = (source, mergeConf, blockUnset = false, excludeKeys) => {
if ((0, type_utils_1.isString)(mergeConf)) {
mergeConf = { scalar: mergeConf };
}
const deepConf = {
scalar: (mergeConf === null || mergeConf === void 0 ? void 0 : mergeConf.scalar) || merge_low_1.UpdateCode.B,
vector: (mergeConf === null || mergeConf === void 0 ? void 0 : mergeConf.vector) || merge_low_1.UpdateCode.XS,
nested: (mergeConf === null || mergeConf === void 0 ? void 0 : mergeConf.nested) || merge_low_1.UpdateCode.N,
};
const globKeys = (0, datum_utils_1.getObjectKeys)(mergeConf)
.filter((s) => s.includes("*"))
.sort((s1, s2) => s2.length - s1.length);
const mergeCodes = {};
const sourceKeys = (0, datum_utils_1.getObjectKeys)(source);
for (const srcLabel of sourceKeys) {
const srcValue = source[srcLabel];
const labelConf = mergeConf === null || mergeConf === void 0 ? void 0 : mergeConf[srcLabel];
if (excludeKeys === null || excludeKeys === void 0 ? void 0 : excludeKeys.includes(srcLabel)) {
mergeCodes[srcLabel] = merge_low_1.UpdateCode.N;
continue;
}
if ((0, type_utils_1.isNullish)(srcValue) && blockUnset) {
mergeCodes[srcLabel] = merge_low_1.UpdateCode.N;
continue;
}
if ((0, type_utils_1.isString)(labelConf)) {
mergeCodes[srcLabel] = labelConf;
continue;
}
if ((0, type_utils_1.isObject)(labelConf) && (0, type_utils_1.isObject)(srcValue)) {
mergeCodes[srcLabel] = (0, exports.fillUpdateCodes)(srcValue, Object.assign(Object.assign({}, deepConf), labelConf), blockUnset);
continue;
}
const globIndex = globKeys.findIndex((g) => (0, datum_utils_1.fastGlobMatch)(g, srcLabel));
if (globIndex >= 0) {
const globConf = mergeConf[globKeys[globIndex]];
if ((0, type_utils_1.isString)(globConf)) {
mergeCodes[srcLabel] = globConf;
continue;
}
if ((0, type_utils_1.isObject)(globConf) && (0, type_utils_1.isObject)(srcValue)) {
mergeCodes[srcLabel] = (0, exports.fillUpdateCodes)(srcValue, Object.assign(Object.assign({}, deepConf), globConf), blockUnset);
continue;
}
}
if ((0, type_utils_1.isObject)(srcValue)) {
mergeCodes[srcLabel] = deepConf.nested;
}
else if ((0, type_utils_1.isArrayOfAny)(srcValue)) {
mergeCodes[srcLabel] = deepConf.vector;
}
else {
mergeCodes[srcLabel] = deepConf.scalar;
}
}
if ((0, datum_utils_1.getObjectKeys)(mergeCodes).length !== sourceKeys.length) {
throw new Error("illegal merge config state");
}
return mergeCodes;
};
exports.fillUpdateCodes = fillUpdateCodes;