@rimbu/deep
Version:
Tools to use handle plain JS objects as immutable objects
126 lines • 4.53 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.patchAt = exports.getAt = exports.Path = void 0;
var tslib_1 = require("tslib");
var internal_cjs_1 = require("./internal.cjs");
var Path;
(function (Path) {
/**
* Regular expression used to split a path string into tokens.
*/
Path.stringSplitRegex = /\?\.|\.|\[|\]/g;
/**
* Return the given `path` string split into an array of subpaths.
* @param path - the input string path
*/
function stringSplit(path) {
return path.split(Path.stringSplitRegex);
}
Path.stringSplit = stringSplit;
})(Path || (exports.Path = Path = {}));
/**
* Returns the value resulting from selecting the given `path` in the given `source` object.
* It supports optional chaining for nullable values or values that may be undefined, and also
* for accessing objects inside an array.
* There is currently no support for forcing non-null (the `!` operator).
* @typeparam T - the object type to select in
* @typeparam P - a Path in object type T
* @param source - the object to select in
* @param path - the path into the object
* @example
* ```ts
* const value = { a: { b: { c: [{ d: 5 }, { d: 6 }] } } }
* Deep.getAt(value, 'a.b');
* // => { c: 5 }
* Deep.getAt(value, 'a.b.c');
* // => [{ d: 5 }, { d: 5 }]
* Deep.getAt(value, 'a.b.c[1]');
* // => { d: 6 }
* Deep.getAt(value, 'a.b.c[1]?.d');
* // => 6
* ```
*/
function getAt(source, path) {
var e_1, _a;
if (path === '') {
// empty path always directly returns source value
return source;
}
var items = Path.stringSplit(path);
// start with `source` as result value
var result = source;
try {
for (var items_1 = tslib_1.__values(items), items_1_1 = items_1.next(); !items_1_1.done; items_1_1 = items_1.next()) {
var item = items_1_1.value;
if (undefined === item || item === '' || item === '[') {
// ignore irrelevant items
continue;
}
if (undefined === result || null === result) {
// optional chaining assumed and no value available, skip rest of path and return undefined
return undefined;
}
// set current result to subpath value
result = result[item];
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (items_1_1 && !items_1_1.done && (_a = items_1.return)) _a.call(items_1);
}
finally { if (e_1) throw e_1.error; }
}
return result;
}
exports.getAt = getAt;
/**
* Patches the value at the given path in the source to the given value.
* Because the path to update must exist in the `source` object, optional
* chaining and array indexing is not allowed.
* @param source - the object to update
* @param path - the path in the object to update
* @param patchItem - the patch for the value at the given path
* @example
* ```ts
* const value = { a: { b: { c: 5 } } };
* Deep.patchAt(value, 'a.b.c', v => v + 5);
* // => { a: { b: { c: 6 } } }
* ```
*/
function patchAt(source, path, patchItem) {
if (path === '') {
return internal_cjs_1.Deep.patch(source, patchItem);
}
var items = Path.stringSplit(path);
// creates a patch object based on the current path
function createPatchPart(index, target) {
var _a;
if (index === items.length) {
// processed all items, return the input `patchItem`
return patchItem;
}
var item = items[index];
if (undefined === item || item === '') {
// empty items can be ignored
return createPatchPart(index + 1, target);
}
if (item === '[') {
// next item is array index, set arrayMode to true
return createPatchPart(index + 1, target);
}
// create object with subPart as property key, and the restuls of processing next parts as value
var result = (_a = {},
_a[item] = createPatchPart(index + 1, target[item]),
_a);
if (Array.isArray(target)) {
// target in source object is array/tuple, so the patch should be object
return result;
}
// target in source is not an array, so it patch should be an array
return [result];
}
return internal_cjs_1.Deep.patch(source, createPatchPart(0, source));
}
exports.patchAt = patchAt;
//# sourceMappingURL=path.cjs.map
;