@rimbu/deep
Version:
Tools to use handle plain JS objects as immutable objects
132 lines • 5.2 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.patch = patch;
var tslib_1 = require("tslib");
var base_1 = require("@rimbu/base");
/**
* Returns an immutably updated version of the given `value` where the given `patchItems` have been
* applied to the result.
* The Rimbu patch notation is as follows:
* - if the target is a simple value or array, the patch can be the same type or a function returning the same type
* - if the target is a tuple (array of fixed length), the patch be the same type or an object containing numeric keys with patches indicating the tuple index to patch
* - if the target is an object, the patch can be the same type, or an array containing partial keys with their patches for the object
* @typeparam T - the type of the value to patch
* @typeparam TE - a utility type
* @typeparam TT - a utility type
* @param value - the input value to patch
* @param patchItem - the `Patch` value to apply to the input value
* @example
* ```ts
* const input = { a: 1, b: { c: true, d: 'a' } }
* patch(input, [{ a: 2 }]) // => { a: 2, b: { c: true, d: 'a' } }
* patch(input, [{ b: [{ c: (v) => !v }] }] )
* // => { a: 1, b: { c: false, d: 'a' } }
* patch(input: [{ a: (v) => v + 1, b: [{ d: 'q' }] }] )
* // => { a: 2, b: { c: true, d: 'q' } }
* ```
*/
function patch(value, patchItem) {
return patchEntry(value, value, value, patchItem);
}
function patchEntry(value, parent, root, patchItem) {
if (Object.is(value, patchItem)) {
// patching a value with itself never changes the value
return value;
}
if (typeof value === 'function') {
// function input, directly return patch
return patchItem;
}
if (typeof patchItem === 'function') {
// function patch always needs to be resolved first
var item = patchItem(value, parent, root);
return patchEntry(value, parent, root, item);
}
if ((0, base_1.isPlainObj)(value)) {
// value is plain object
return patchPlainObj(value, root, patchItem);
}
if (Array.isArray(value)) {
// value is tuple or array
return patchArr(value, root, patchItem);
}
// value is primitive type or complex object
return patchItem;
}
function patchPlainObj(value, root, patchItem) {
var e_1, _a;
if (!Array.isArray(patchItem)) {
// the patch is a complete replacement of the current value
return patchItem;
}
// patch is an array of partial updates
// copy the input value
var result = tslib_1.__assign({}, value);
var anyChange = false;
try {
// loop over patches in array
for (var patchItem_1 = tslib_1.__values(patchItem), patchItem_1_1 = patchItem_1.next(); !patchItem_1_1.done; patchItem_1_1 = patchItem_1.next()) {
var entry = patchItem_1_1.value;
// update root if needed
var currentRoot = value === root ? tslib_1.__assign({}, result) : root;
// keep current updated result as parent
var parent = tslib_1.__assign({}, result);
// loop over all the patch keys
for (var key in entry) {
// patch the value at the given key with the patch at that key
var currentValue = result[key];
var newValue = patchEntry(currentValue, parent, currentRoot, entry[key]);
if (!Object.is(currentValue, newValue)) {
// if value changed, set it in result and mark change
anyChange = true;
result[key] = newValue;
}
}
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (patchItem_1_1 && !patchItem_1_1.done && (_a = patchItem_1.return)) _a.call(patchItem_1);
}
finally { if (e_1) throw e_1.error; }
}
if (anyChange) {
// something changed, return new value
return result;
}
// nothing changed, return old value
return value;
}
function patchArr(value, root, patchItem) {
if (Array.isArray(patchItem)) {
// value is a normal array
// patch is a complete replacement of current array
return patchItem;
}
// value is a tuple
// patch is an object containing numeric keys with function values
// that update the tuple at the given indices
// copy the tuple
var result = tslib_1.__spreadArray([], tslib_1.__read(value), false);
var anyChange = false;
// loop over all index keys in object
for (var index in patchItem) {
var numIndex = index;
// patch the tuple at the given index
var currentValue = result[numIndex];
var newValue = patchEntry(currentValue, value, root, patchItem[index]);
if (!Object.is(newValue, currentValue)) {
// if value changed, set it in result and mark change
anyChange = true;
result[numIndex] = newValue;
}
}
if (anyChange) {
// something changed, return new value
return result;
}
// nothing changed, return old value
return value;
}
//# sourceMappingURL=patch.cjs.map