Observed since 20 Oct, 18:21 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
Pure Python ECMAScript 2026 and JSX traversal for native and mapping ESTree ASTs
A pure Python semantic walker for ECMAScript ESTree ASTs, targeting node shapes used by ECMAScript 2026 and the explicit JSX extension. Python 3.11+; no runtime dependencies. Native parser objects and deserialized mappings are accepted without copying nodes or mutating the AST.
python -m pip install "acornwalk>=2,<3"
The 2.x release preserves the import and camelCase APIs and requires Python
3.11 or newer. It has no runtime dependencies. For native parser ASTs, install
esprima-fork 5.x separately (imported as esprima) and request range=True
when using position searches. See the
migration notes
for the changes to searches, callback state and result objects.
import acornwalk as walk
ast = {"type": "Program", "range": [0, 2], "body": [
{"type": "ExpressionStatement", "range": [0, 2], "expression":
{"type": "Identifier", "name": "x", "range": [0, 1]}}
]}
names = []
walk.simple(ast, {"Identifier": lambda node, state: state.append(node["name"])}, state=names)
assert names == ["x"]
assert walk.findNodeAt(ast, 0, 1, "Identifier").node is ast["body"][0]["expression"]
| API | Callback or result |
|---|---|
simple(node, visitors, baseVisitor=None, state=None, override=None) | Postorder visitor(node, state) |
ancestor(node, visitors, baseVisitor=None, state=None, override=None) | Postorder visitor(node, state, ancestors) |
recursive(node, state, funcs, baseVisitor=None, override=None) | visitor(node, state, recurse) controls child recursion |
full(node, callback, baseVisitor=None, state=None, override=None) | callback(node, state, category) |
fullAncestor(node, callback, baseVisitor=None, state=None, override=None) | callback(node, state, ancestors, category) |
make(funcs, baseVisitor=None) | Independent handler overlay |
findNodeAt(node, start, end, test=None, baseVisitor=None, state=None) | Innermost exact range match; None bounds are wildcards |
findNodeAround(node, pos, test=None, baseVisitor=None, state=None) | Innermost containing match, including end boundary |
findNodeAfter(node, pos, test=None, baseVisitor=None, state=None) | First outermost match starting at/after pos |
findNodeBefore(node, pos, test=None, baseVisitor=None, state=None) | Greatest end at/before pos; ties favor outer nodes |
Searches return Found(node, state) or None. Tests are node type strings,
None for any type, or (category, node) -> bool callables. Callback exceptions
propagate. Ancestor stacks contain the current node and are reused; copy the
list when retaining it. State None supplies ancestors as state for ancestor
walkers. Explicit False, 0, "", {} and [] remain the supplied state.
seen = []
walk.fullAncestor(ast, lambda node, state, stack, category:
seen.append((walk.getField(node, "type"), len(stack))))
walk.recursive(ast, names, {"Identifier": lambda node, state, recurse:
state.append(walk.getField(node, "name"))})
base = walk.make({"MyExtension": lambda node, state, recurse:
recurse(walk.getField(node, "argument"), state, "Expression")})
walk.simple({"type": "MyExtension", "argument": ast["body"][0]["expression"]}, {}, base)
Visitor keys may be concrete types or categories such as Expression,
Statement, Pattern and VariablePattern. Bindings use VariablePattern;
ordinary references use Identifier. Noncomputed keys, labels, import binding
metadata, export specifier metadata and meta-property names are skipped as
evaluated references. Import attributes visit their value, not their key.
This is semantic traversal, not an exhaustive serialization visitor.
Custom bases may be mappings, Base objects or overlays. Instance and subclass
overrides are honored. Base handlers have the unbound signature
(node, state, recurse); subclass handlers should use that signature or
@staticmethod. make does not mutate the supplied base. Unknown types raise
ValueError naming the type and the extension mechanism.
Positions are nonnegative integer UTF-16 code-unit offsets. Searches use
start/end or two-element range; start/end take precedence. Astral characters
occupy two units. Offsets are read as supplied, not converted from Python scalar
indices. With esprima-fork, request range=True. Missing/invalid positions
raise ValueError. nodeRange(node) exposes this accessor. getField(node, name, default=None) reads mapping/attribute fields and native isAsync/async_
and allowAwait/await_ aliases. An explicit canonical mapping field takes precedence.
Missing native fields honor the supplied default even for esprima's permissive
attribute lookup; declared null fields retain None.
Modern traversal covers object rest/spread, async functions/generators,
for-await metadata, chains, private names, public/private fields, static blocks,
module attributes and dynamic import options. Operators and literal metadata
use ordinary expression/leaf shapes. Legacy FieldDefinition is accepted as a
PropertyDefinition alias. Explicit JSX handlers cover elements, fragments,
names, attributes, expression containers, spread attributes/children and empty
expressions. JSX names keep their JSX-specific node types.
TypeScript, Flow, decorators and other proposal nodes require custom visitors. The walker does not validate grammar or editions. Mechanically walking an unknown declaration kind with existing node shapes does not advertise parser support for proposals. Circular graphs and excessive recursion depth are outside the AST contract.
See contributor instructions
and CI results.
Core parity is pinned
to acorn-walk in acorn 8.15.0.
Python differences include None state handling, mappings/ranges, validated
offsets, native aliases and the documented JSX extension.
Performance measurements compare the original, modern starting and current versions, including the old destructuring omission and remaining native validation cost.
Release automation and version bumps are documented in RELEASING.md.
pypistats · 49 observations shown. Missing days are unknown. Source category: overall: without mirrors.
| Date / source period | Value | Observed at |
|---|---|---|
| 2026-10-20 – 2026-10-20 | 6 | 2026-10-21T11:57:14.6767860Z |
| 2026-10-19 – 2026-10-19 | 1 | 2026-10-21T11:57:14.6767860Z |
| 2026-10-18 – 2026-10-18 | 1 | 2026-10-21T11:57:14.6767860Z |
| 2026-10-17 – 2026-10-17 | 4 | 2026-10-21T11:57:14.6767860Z |
| 2026-10-16 – 2026-10-16 | 4 | 2026-10-21T11:57:14.6767860Z |
| 2026-10-15 – 2026-10-15 | 5 | 2026-10-21T11:57:14.6767860Z |
| 2026-10-14 – 2026-10-14 | 14 | 2026-10-21T11:57:14.6767860Z |
| 2026-10-13 – 2026-10-13 | 75 | 2026-10-21T11:57:14.6767860Z |
| 2026-10-12 – 2026-10-12 | 1 | 2026-10-21T11:57:14.6767860Z |
| 2026-10-11 – 2026-10-11 | 2 | 2026-10-21T11:57:14.6767860Z |
| 2026-10-10 – 2026-10-10 | 1 | 2026-10-21T11:57:14.6767860Z |
| 2026-10-09 – 2026-10-09 | 1 | 2026-10-21T11:57:14.6767860Z |
| 2026-10-08 – 2026-10-08 | 1 | 2026-10-21T11:57:14.6767860Z |
| 2026-10-07 – 2026-10-07 | 1 | 2026-10-21T11:57:14.6767860Z |
| 2026-10-06 – 2026-10-06 | 1 | 2026-10-21T11:57:14.6767860Z |
| 2026-10-01 – 2026-10-01 | 3 | 2026-10-21T11:57:14.6767860Z |
| 2026-09-30 – 2026-09-30 | 2 | 2026-10-21T11:57:14.6767860Z |
| 2026-09-27 – 2026-09-27 | 1 | 2026-10-21T11:57:14.6767860Z |
| 2026-09-26 – 2026-09-26 | 4 | 2026-10-21T11:57:14.6767860Z |
| 2026-09-24 – 2026-09-24 | 2 | 2026-10-21T11:57:14.6767860Z |
| 2026-09-23 – 2026-09-23 | 2 | 2026-10-21T11:57:14.6767860Z |
| 2026-09-22 – 2026-09-22 | 3 | 2026-10-21T11:57:14.6767860Z |
| 2026-09-12 – 2026-09-12 | 1 | 2026-10-21T11:57:14.6767860Z |
| 2026-09-10 – 2026-09-10 | 1 | 2026-10-21T11:57:14.6767860Z |
| 2026-09-09 – 2026-09-09 | 2 | 2026-10-21T11:57:14.6767860Z |
| 2026-09-07 – 2026-09-07 | 1 | 2026-10-21T11:57:14.6767860Z |
| 2026-09-06 – 2026-09-06 | 2 | 2026-10-21T11:57:14.6767860Z |
| 2026-09-03 – 2026-09-03 | 2 | 2026-10-21T11:57:14.6767860Z |
| 2026-08-31 – 2026-08-31 | 2 | 2026-10-21T11:57:14.6767860Z |
| 2026-08-28 – 2026-08-28 | 1 | 2026-10-21T11:57:14.6767860Z |
| 2026-08-27 – 2026-08-27 | 1 | 2026-10-21T11:57:14.6767860Z |
| 2026-08-24 – 2026-08-24 | 8 | 2026-10-21T11:57:14.6767860Z |
| 2026-08-23 – 2026-08-23 | 3 | 2026-10-21T11:57:14.6767860Z |
| 2026-08-20 – 2026-08-20 | 1 | 2026-10-21T11:57:14.6767860Z |
| 2026-08-19 – 2026-08-19 | 3 | 2026-10-21T11:57:14.6767860Z |
| 2026-08-18 – 2026-08-18 | 18 | 2026-10-21T11:57:14.6767860Z |
| 2026-08-17 – 2026-08-17 | 3 | 2026-10-21T11:57:14.6767860Z |
| 2026-08-14 – 2026-08-14 | 3 | 2026-10-21T11:57:14.6767860Z |
| 2026-08-13 – 2026-08-13 | 2 | 2026-10-21T11:57:14.6767860Z |
| 2026-08-08 – 2026-08-08 | 1 | 2026-10-21T11:57:14.6767860Z |
| 2026-08-07 – 2026-08-07 | 1 | 2026-10-21T11:57:14.6767860Z |
| 2026-08-05 – 2026-08-05 | 1 | 2026-10-21T11:57:14.6767860Z |
| 2026-08-04 – 2026-08-04 | 3 | 2026-10-21T11:57:14.6767860Z |
| 2026-08-03 – 2026-08-03 | 4 | 2026-10-21T11:57:14.6767860Z |
| 2026-08-02 – 2026-08-02 | 1 | 2026-10-21T11:57:14.6767860Z |
| 2026-08-01 – 2026-08-01 | 1 | 2026-10-21T11:57:14.6767860Z |
| 2026-07-27 – 2026-07-27 | 1 | 2026-10-21T11:57:14.6767860Z |
| 2026-07-26 – 2026-07-26 | 1 | 2026-10-21T11:57:14.6767860Z |
| 2026-07-23 – 2026-07-23 | 1 | 2026-10-21T11:57:14.6767860Z |
| Version | Published | Direct dependencies | Open version |
|---|---|---|---|
| 2.0.0 | 13 Oct 2026 | 0 |
This describes our collected data; it is not a security assessment.
Observed since 20 Oct, 18:21 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package:2911bbef96708ac07f9b3d517da9c9d88c79bb1695334e7ea080d44de9ed84ac