This package is retired. The published package, documentation, examples, and changelog remain available.
Migration to ast-monkey
This standalone package is retired. ast-monkey@10.0.0 owns the transformer
and provides the lightweight ast-monkey/traverse entry point. The documentation
below describes the archived ast-monkey-traverse@4.3.1 release. Its published
files, examples, and
changelog remain available.
Install the maintained package:
npm install ast-monkey@^10.0.0
Change the import while keeping your callback:
import { DELETE, traverse } from "ast-monkey/traverse";
Migrate traverse, DELETE, and callback types together. The canonical root
and subpath share one TypeScript DELETE identity, but the legacy package’s
unique symbol declaration remains separate. Mixing canonical and legacy
token or callback-type imports fails TypeScript assignment despite the shared
runtime symbol registry key.
The transformed result, supported tree model, exact paths, lazy parent
snapshots, immediate stopping, and shared DELETE token retain their existing
contracts. Validation errors use ast-monkey/traverse(): [THROW_ID_XX] instead
of the old package prefix. Invalid trees and replacements use THROW_ID_01,
and invalid callbacks use THROW_ID_02; the standalone transformer uses the
reverse numbering. Exceptions thrown by callbacks remain unchanged.
See the consolidated migration guide
for the observer mapping and entry-point choices.
The published standalone browser artifact remains
ast-monkey-traverse.umd.js, with the global astMonkeyTraverse.
Purpose
Traverse, read, and edit a supported recursive tree value.
Supported tree model
The input must be an acyclic tree built from ordinary arrays, ordinary object-literal objects, and supported scalar values. Object properties must be own, enumerable, string-keyed data properties. The valid own data key __proto__ is preserved without changing an object’s prototype.
The same object or array cannot appear in two positions. Cycles and repeated references are graphs rather than trees, so traverse() rejects them before the first callback with THROW_ID_02. It also rejects symbol keys, non-enumerable properties, accessors, extra array properties, custom or null prototypes, class instances, functions, bigint, symbol, Date, Map, and Set values. Accessor getters are not invoked during validation, and inherited properties are never traversed.
Array holes and explicit undefined entries are different. Holes remain sparse and do not trigger callbacks. An own array element whose value is undefined is preserved, visited, and can be replaced or deleted like any other value. Later indexes do not shift unless a callback explicitly deletes an element.
Deep trees and callback metadata
Traversal uses an explicit work stack, so deeply nested supported trees do not depend on the JavaScript call stack. The package test suite includes an acyclic object nested 10,000 levels deep.
innerObj.parent is materialized lazily. Read it during the callback if you
need that callback’s parent state; once materialized, the detached read-only
view remains stable. The package stores versioned changes instead of cloning
every remaining subtree for every callback.
Path metadata is prepared directly for ordinary depths. On very deep nodes, path and pathSegments are materialized only when you read them, avoiding unbounded prefix-copying work. For a long synchronous traversal, the required node callback is also the progress and cancellation point: count visits there, and set stop.now = true when no more nodes are needed.
API — traverse()
The main function traverse() is imported like this:
The function accepts a supported recursive tree value and a callback:
traverse() requires a callback even for a primitive root. A primitive,
null, or undefined root is cloned or returned without invoking the callback;
the root container itself is not visited and cannot be deleted. An invalid
callback throws THROW_ID_01; invalid input or a callback replacement throws
THROW_ID_02.
where the callback has the following API:
The callback API is similar to Array.forEach():
import { DELETE, traverse } from "ast-monkey-traverse";
let ast = [{ a: "b", c: "d" }];
ast = traverse(ast, (key, val, innerObj, stop) => {
const current = innerObj.parentType === "array" ? key : val;
// Return the current value to keep it, another supported value to replace it,
// or DELETE to remove this node.
return current === "d" ? DELETE : current;
});
Always return the value that should occupy the current position. A callback with no explicit return writes undefined there. You can also edit a callback-visible object or array in place and return that same container; later callback metadata reflects synchronous in-place edits.
To delete the current node, return the exported DELETE token. The token uses the shared symbol registry, so it remains interoperable across duplicate package copies in the same JavaScript realm. NaN is ordinary numeric data and is preserved. Numeric replacements also preserve the distinction between +0 and -0.
import { DELETE, traverse } from "ast-monkey-traverse";
const result = traverse([Number.NaN, "remove"], (value) =>
value === "remove" ? DELETE : value,
);
// => [NaN]
API — version
You can import version:
API — types
This package is written in TypeScript and exports the following types:
| Type | Description |
|---|---|
CallbackType: Callback | |
Callback | The callback signature. It returns a supported tree value or the exported DELETE token. |
InnerObjType: InnerObj | |
InnerObj | Traversal metadata, including exact and display paths, parent context, depth, and the topmost key. |
StopType: Stop | |
Stop | The shared stop token. Set its now key to true to end traversal. |
TreeValueType: TreeValue | |
TreeValue | Any supported recursive tree value. |
TreeArrayType: TreeArray | |
TreeArray | A mutable array of TreeValue entries. |
TreeObjectType: TreeObject | |
TreeObject | A string-keyed object containing TreeValue entries. |
TreePrimitiveType: TreePrimitive | |
TreePrimitive | A supported scalar: string, number, boolean, null, or undefined. |
ReadonlyTreeValueType: ReadonlyTreeValue | |
ReadonlyTreeValue | The recursive read-only form used by parent metadata. |
ReadonlyTreeContainerType: ReadonlyTreeContainer | |
ReadonlyTreeContainer | A read-only parent array or object. |
ReadonlyTreeArrayType: ReadonlyTreeArray | |
ReadonlyTreeArray | The recursive read-only array form. |
ReadonlyTreeObjectType: ReadonlyTreeObject | |
ReadonlyTreeObject | The recursive read-only string-keyed object form. |
import type {
Callback,
InnerObj,
ReadonlyTreeArray,
ReadonlyTreeContainer,
ReadonlyTreeObject,
ReadonlyTreeValue,
Stop,
TreeArray,
TreeObject,
TreePrimitive,
TreeValue,
} from "ast-monkey-traverse";
innerObj in the callback
When you call traverse() like this:
input = traverse(input, (key, val, innerObj, stop) => {
...
})
you get four variables:
keyvalinnerObjstop— setstop.now = true;to stop the traversal
When traversing an object, key is its current string key and val is the corresponding value. When traversing an array, key is the current array element—including an explicit undefined—and val is undefined. Use innerObj.parentType to distinguish the two modes because an object property can itself contain undefined.
innerObj key | Type | Description |
|---|---|---|
depthType: Integer | ||
depth | Integer | Zero is the root level. Each nested container increments the value by 1. |
pathType: String | ||
path | String | A legacy dot-joined display path. It remains compatible for ordinary keys but is ambiguous when a key contains a dot or is empty. |
pathSegmentsType: Read-only string array | ||
pathSegments | Read-only string array | The exact path. Use this field for programmatic lookup because each object key and array index occupies one segment. |
topmostKeyType: Optional string | ||
topmostKey | Optional string | The root object key that contains the current value. It is absent when traversing a root array. |
parentType: Read-only container | ||
parent | Read-only container | A lazily materialized detached view of the containing array or object. Read it during the callback to capture that callback’s state; afterward, the materialized view remains stable. Runtime write attempts are ignored, and TypeScript exposes it as read-only. |
parentTypeType: "array" | "object" | ||
parentType | "array" | "object" | The kind of container currently being traversed. |
parentKeyType: String or null | ||
parentKey | String or null | The key or array index that locates the parent container in its own parent. It is null at the root. |
For exact lookup, pass pathSegments directly to object-path:
import objectPath from "object-path";
import { traverse } from "ast-monkey-traverse";
const input = { "a.b": { c: 1 } };
traverse(input, (key, val, innerObj) => {
const current = innerObj.parentType === "array" ? key : val;
console.log(objectPath.get(input, innerObj.pathSegments));
return current;
});
To amend an exact path before lookup, pass pathSegments directly to an ast-monkey-util helper’s array overload. Don’t join the segments first: the returned array preserves dotted and empty keys and can be passed directly to object-path.
Stopping
Here’s how to stop the traversal. First, gather the legacy display paths (arrays use dots too, as in a.1.b instead of a[1].b):
import { traverse } from "ast-monkey-traverse";
const input = { a: "1", b: { c: "2" } };
const gathered = [];
traverse(input, (key1, val1, innerObj) => {
const current = innerObj.parentType === "array" ? key1 : val1;
gathered.push(innerObj.path);
return current;
});
console.log(gathered);
// => ["a", "b", "b.c"]
All paths were gathered: ["a", "b", "b.c"].
Let’s force the program to stop at the path “b”:
import { traverse } from "ast-monkey-traverse";
const input = { a: "1", b: { c: "2" } };
const gathered = [];
traverse(input, (key1, val1, innerObj, stop) => {
const current = innerObj.parentType === "array" ? key1 : val1;
gathered.push(innerObj.path);
if (innerObj.path === "b") {
stop.now = true; // <---------------- !!!!!!!!!!
}
return current;
});
console.log(gathered);
// => ["a", "b"]
Notice how there were no more gathered paths after “b”, only ["a", "b"].
Compared to ast-monkey-traverse-with-lookahead
The transformer uses callback return values to replace or delete entries in a clone. It does not preview future visits because replacements can change what will be visited next.
The observer ignores callback return values and can report upcoming visits. Its callback values can reference original input, so mutating them can change that input. It visits sparse holes and drains buffered callbacks after a stop request. Choose the observer contract when those semantics match your task.