// src/index.ts var to_string = (obj) => Object.prototype.toString.call(obj); var is_typed_array = (value) => ArrayBuffer.isView(value) && !(value instanceof DataView); var is_date = (obj) => to_string(obj) === "[object Date]"; var is_regexp = (obj) => to_string(obj) === "[object RegExp]"; var is_error = (obj) => to_string(obj) === "[object Error]"; var is_boolean = (obj) => to_string(obj) === "[object Boolean]"; var is_number = (obj) => to_string(obj) === "[object Number]"; var is_string = (obj) => to_string(obj) === "[object String]"; var is_array = Array.isArray; var gopd = Object.getOwnPropertyDescriptor; var is_property_enumerable = Object.prototype.propertyIsEnumerable; var get_own_property_symbols = Object.getOwnPropertySymbols; var has_own_property = Object.prototype.hasOwnProperty; function own_enumerable_keys(obj) { const res = Object.keys(obj); const symbols = get_own_property_symbols(obj); for (let i = 0; i < symbols.length; i++) { if (is_property_enumerable.call(obj, symbols[i])) { res.push(symbols[i]); } } return res; } function is_writable(object, key) { return !gopd(object, key)?.writable; } function copy(src, options) { if (typeof src === "object" && src !== null) { let dst; if (is_array(src)) { dst = []; } else if (is_date(src)) { dst = new Date(src.getTime ? src.getTime() : src); } else if (is_regexp(src)) { dst = new RegExp(src); } else if (is_error(src)) { dst = { message: src.message }; } else if (is_boolean(src) || is_number(src) || is_string(src)) { dst = Object(src); } else if (is_typed_array(src)) { return src.slice(); } else { dst = Object.create(Object.getPrototypeOf(src)); } const iterator_function = options.includeSymbols ? own_enumerable_keys : Object.keys; for (const key of iterator_function(src)) { dst[key] = src[key]; } return dst; } return src; } var empty_null = { includeSymbols: false, immutable: false }; function walk(root, cb, options = empty_null) { const path = []; const parents = []; let alive = true; const iterator_function = options.includeSymbols ? own_enumerable_keys : Object.keys; const immutable = !!options.immutable; return function walker(node_) { const node = immutable ? copy(node_, options) : node_; const modifiers = {}; let keep_going = true; const state = { node, node_, path: [].concat(path), parent: parents[parents.length - 1], parents, key: path[path.length - 1], isRoot: path.length === 0, level: path.length, circular: void 0, isLeaf: false, notLeaf: true, notRoot: true, isFirst: false, isLast: false, update: function(x, stopHere = false) { if (!state.isRoot) { state.parent.node[state.key] = x; } state.node = x; if (stopHere) { keep_going = false; } }, delete: function(stopHere) { delete state.parent.node[state.key]; if (stopHere) { keep_going = false; } }, remove: function(stopHere) { if (is_array(state.parent.node)) { state.parent.node.splice(state.key, 1); } else { delete state.parent.node[state.key]; } if (stopHere) { keep_going = false; } }, keys: null, before: function(f) { modifiers.before = f; }, after: function(f) { modifiers.after = f; }, pre: function(f) { modifiers.pre = f; }, post: function(f) { modifiers.post = f; }, stop: function() { alive = false; }, block: function() { keep_going = false; } }; if (!alive) { return state; } function update_state() { if (typeof state.node === "object" && state.node !== null) { if (!state.keys || state.node_ !== state.node) { state.keys = iterator_function(state.node); } state.isLeaf = state.keys.length === 0; for (let i = 0; i < parents.length; i++) { if (parents[i].node_ === node_) { state.circular = parents[i]; break; } } } else { state.isLeaf = true; state.keys = null; } state.notLeaf = !state.isLeaf; state.notRoot = !state.isRoot; } update_state(); const ret = cb.call(state, state.node); if (ret !== void 0 && state.update) { state.update(ret); } if (modifiers.before) { modifiers.before.call(state, state.node); } if (!keep_going) { return state; } if (typeof state.node === "object" && state.node !== null && !state.circular) { parents.push(state); update_state(); for (const [index, key] of Object.entries(state.keys ?? [])) { path.push(key); if (modifiers.pre) { modifiers.pre.call(state, state.node[key], key); } const child = walker(state.node[key]); if (immutable && has_own_property.call(state.node, key) && !is_writable(state.node, key)) { state.node[key] = child.node; } child.isLast = state.keys?.length ? +index === state.keys.length - 1 : false; child.isFirst = +index === 0; if (modifiers.post) { modifiers.post.call(state, child); } path.pop(); } parents.pop(); } if (modifiers.after) { modifiers.after.call(state, state.node); } return state; }(root).node; } var Traverse = class { // ! Have to keep these public as legacy mode requires them #value; #options; constructor(obj, options = empty_null) { this.#value = obj; this.#options = options; } /** * Get the element at the array `path`. */ get(paths) { let node = this.#value; for (let i = 0; node && i < paths.length; i++) { const key = paths[i]; if (!has_own_property.call(node, key) || !this.#options.includeSymbols && typeof key === "symbol") { return void 0; } node = node[key]; } return node; } /** * Return whether the element at the array `path` exists. */ has(paths) { let node = this.#value; for (let i = 0; node && i < paths.length; i++) { const key = paths[i]; if (!has_own_property.call(node, key) || !this.#options.includeSymbols && typeof key === "symbol") { return false; } node = node[key]; } return true; } /** * Set the element at the array `path` to `value`. */ set(path, value) { let node = this.#value; let i = 0; for (i = 0; i < path.length - 1; i++) { const key = path[i]; if (!has_own_property.call(node, key)) { node[key] = {}; } node = node[key]; } node[path[i]] = value; return value; } /** * Execute `fn` for each node in the object and return a new object with the results of the walk. To update nodes in the result use `this.update(value)`. */ map(cb) { return walk(this.#value, cb, { immutable: true, includeSymbols: !!this.#options.includeSymbols }); } /** * Execute `fn` for each node in the object but unlike `.map()`, when `this.update()` is called it updates the object in-place. */ forEach(cb) { this.#value = walk(this.#value, cb, this.#options); return this.#value; } /** * For each node in the object, perform a [left-fold](http://en.wikipedia.org/wiki/Fold_(higher-order_function)) with the return value of `fn(acc, node)`. * * If `init` isn't specified, `init` is set to the root object for the first step and the root element is skipped. */ reduce(cb, init) { const skip = arguments.length === 1; let acc = skip ? this.#value : init; this.forEach(function(x) { if (!this.isRoot || !skip) { acc = cb.call(this, acc, x); } }); return acc; } /** * Return an `Array` of every possible non-cyclic path in the object. * Paths are `Array`s of string keys. */ paths() { const acc = []; this.forEach(function() { acc.push(this.path); }); return acc; } /** * Return an `Array` of every node in the object. */ nodes() { const acc = []; this.forEach(function() { acc.push(this.node); }); return acc; } /** * Create a deep clone of the object. */ clone() { const parents = []; const nodes = []; const options = this.#options; if (is_typed_array(this.#value)) { return this.#value.slice(); } return function clone(src) { for (let i = 0; i < parents.length; i++) { if (parents[i] === src) { return nodes[i]; } } if (typeof src === "object" && src !== null) { const dst = copy(src, options); parents.push(src); nodes.push(dst); const iteratorFunction = options.includeSymbols ? own_enumerable_keys : Object.keys; for (const key of iteratorFunction(src)) { dst[key] = clone(src[key]); } parents.pop(); nodes.pop(); return dst; } return src; }(this.#value); } }; var traverse = (obj, options) => { return new Traverse(obj, options); }; traverse.get = (obj, paths, options) => { return new Traverse(obj, options).get(paths); }; traverse.set = (obj, path, value, options) => { return new Traverse(obj, options).set(path, value); }; traverse.has = (obj, paths, options) => { return new Traverse(obj, options).has(paths); }; traverse.map = (obj, cb, options) => { return new Traverse(obj, options).map(cb); }; traverse.forEach = (obj, cb, options) => { return new Traverse(obj, options).forEach(cb); }; traverse.reduce = (obj, cb, init, options) => { return new Traverse(obj, options).reduce(cb, init); }; traverse.paths = (obj, options) => { return new Traverse(obj, options).paths(); }; traverse.nodes = (obj, options) => { return new Traverse(obj, options).nodes(); }; traverse.clone = (obj, options) => { return new Traverse(obj, options).clone(); }; var src_default = traverse; export { Traverse, src_default as default };