UNPKG

@rimbu/deep

Version:

Tools to use handle plain JS objects as immutable objects

126 lines 4.53 kB
"use strict"; 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