Skip to Content
  • Website
Codsen
  • Home
  • Open Source
  • Articles
  • About

backOpen Source→ast-monkey-traverse

ast-monkey-traverse4.3.1

Utility library to traverse AST

Downloads per monthChangelogMIT Licenselibera manifesto
  • the top
  • MIGRATION TO…
  • PURPOSE
  • SUPPORTED TRE…
  • DEEP TREES AN…
  • API — TRAVERSE()
  • API — VERSION
  • API — TYPES
  • INNEROBJ IN TH…
  • STOPPING
  • COMPARED TO…
  • Changelog

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:

TypeDescription
Callback
Type: Callback
CallbackThe callback signature. It returns a supported tree value or the exported DELETE token.
InnerObj
Type: InnerObj
InnerObjTraversal metadata, including exact and display paths, parent context, depth, and the topmost key.
Stop
Type: Stop
StopThe shared stop token. Set its now key to true to end traversal.
TreeValue
Type: TreeValue
TreeValueAny supported recursive tree value.
TreeArray
Type: TreeArray
TreeArrayA mutable array of TreeValue entries.
TreeObject
Type: TreeObject
TreeObjectA string-keyed object containing TreeValue entries.
TreePrimitive
Type: TreePrimitive
TreePrimitiveA supported scalar: string, number, boolean, null, or undefined.
ReadonlyTreeValue
Type: ReadonlyTreeValue
ReadonlyTreeValueThe recursive read-only form used by parent metadata.
ReadonlyTreeContainer
Type: ReadonlyTreeContainer
ReadonlyTreeContainerA read-only parent array or object.
ReadonlyTreeArray
Type: ReadonlyTreeArray
ReadonlyTreeArrayThe recursive read-only array form.
ReadonlyTreeObject
Type: ReadonlyTreeObject
ReadonlyTreeObjectThe 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:

  • key
  • val
  • innerObj
  • stop — set stop.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 keyTypeDescription
depth
Type: Integer
depthIntegerZero is the root level. Each nested container increments the value by 1.
path
Type: String
pathStringA legacy dot-joined display path. It remains compatible for ordinary keys but is ambiguous when a key contains a dot or is empty.
pathSegments
Type: Read-only string array
pathSegmentsRead-only string arrayThe exact path. Use this field for programmatic lookup because each object key and array index occupies one segment.
topmostKey
Type: Optional string
topmostKeyOptional stringThe root object key that contains the current value. It is absent when traversing a root array.
parent
Type: Read-only container
parentRead-only containerA 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.
parentType
Type: "array" | "object"
parentType"array" | "object"The kind of container currently being traversed.
parentKey
Type: String or null
parentKeyString or nullThe 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.

Permalink to changelogChangelog

Open Changelog
↑ back to top
back 

Copyright

All rights reserved © Roy Revelt 2026
All our open source packages are under MIT licenceopens in a new tab

Activities

🐛 See a bug? Raise an issueopens in a new tab
💘 Check out the Indiewebopens in a new tab and Libera manifestoopens in a new tab