This document describes the Abstract Syntax Tree (AST) structure used throughout Bun's JavaScript toolchain and the transformation passes applied during transpilation and bundling. The AST is the core data structure that represents parsed JavaScript/TypeScript code and is manipulated by the parser, bundler, and printer.
For information about the lexer and parser that construct the AST, see Lexer and Parser. For information about how the AST is converted back to source code, see Code Generation and Printing.
Bun's AST is organized around three fundamental node types: Expr (expressions), Stmt (statements), and Binding (binding patterns). Each node type is defined as a struct with a tagged union containing the node's specific data.
Figure: AST Core Node Types
The parser (defined in src/js_parser/p.rs) uses generic instantiation P<'a, TS, SCAN> to handle different syntax modes while constructing these nodes.
Sources: src/js_parser/p.rs35-42 src/js_parser/p.rs95-96
The Expr struct represents any JavaScript expression that produces a value. It contains a data field (a tagged union of type E) and a Loc field for source location tracking.
Key Expression Variants in E namespace:
| Variant | Description | Struct Type |
|---|---|---|
Identifier | Variable reference | E::Identifier with ref: Ref |
Binary | Binary operation | E::Binary with left: Expr, right: Expr, op: OpCode |
Call | Function call | E::Call with target: Expr, args: ExprNodeList |
Dot | Property access | E::Dot with target: Expr, name: StoreStr |
If | Conditional (ternary) | E::If with test: Expr, yes: Expr, no: Expr |
JSXElement | JSX element | E::JsxElement |
The parser allocates new Expr nodes using p.new_expr(data, loc).
Sources: src/js_parser/p.rs36-39 src/js_parser/p.rs55 src/js_parser/visit/visit_expr.rs57-83
The Stmt struct represents any JavaScript statement. Like Expr, it contains a data field (tagged union of type S) and a Loc field.
Key Statement Variants in S namespace:
| Variant | Description | Struct Type |
|---|---|---|
Local | Variable declaration | S::Local with kind: LocalKind, decls: DeclList |
Expr | Expression statement | S::Expr with value: Expr |
Return | Return statement | S::Return with value: Option<Expr> |
If | If statement | S::If with test: Expr, yes: Stmt, no: Option<Stmt> |
Function | Function declaration | S::Function with func: G::Fn |
Sources: src/js_parser/p.rs36-39 src/js_parser/visit/visit_stmt.rs89-124
A Binding represents a binding pattern used in variable declarations and destructuring.
Key Binding Variants (B namespace):
| Variant | Description | Example |
|---|---|---|
Identifier | Simple identifier | x |
Array | Array destructuring | [a, b] |
Object | Object destructuring | {x, y} |
Sources: src/js_parser/p.rs36-39 src/js_parser/lower/lower_decorators.rs177
Bun applies transformations using a visitor pattern implemented in src/js_parser/visit/. This pass occurs after parsing and handles symbol resolution, dead code elimination (DCE), and constant folding.
The primary entry points are visit_expr and visit_and_append_stmt. These methods recursively traverse the AST, applying transformations in-place where possible.
visit_expr: Dispatches to specific e_* helpers (e.g., e_call, e_binary) based on the expression tag. src/js_parser/visit/visit_expr.rs34-84visit_and_append_stmt: Processes statements and appends them to a StmtList. It handles block scoping and conditional removal of statements (e.g., dropping debugger if configured). src/js_parser/visit/visit_stmt.rs47-125During the visit pass, Bun performs several optimizations:
process.env.NODE_ENV). src/js_parser/visit/visit_expr.rs141-161if (false) { ... }) are identified using SideEffects analysis and can be omitted from the output. src/js_parser/visit/mod.rs30-32["hey"][0] becomes "hey"). test/bundler/transpiler/transpiler.test.js128-142Figure: Transformation Flow in P::visit
Sources: src/js_parser/visit/visit_expr.rs149-158 src/js_parser/visit/visit_stmt.rs76-79 src/js_parser/visit/visit_stmt.rs82-84
TypeScript-specific nodes like STypeScript (interfaces, type aliases) are simply dropped during the statement visit pass. src/js_parser/visit/visit_stmt.rs76-79
Bun implements the TC39 standard ES decorators by lowering them into a series of variable declarations and runtime helper calls. This logic is contained in src/js_parser/lower/lower_decorators.rs.
__decorateClass).PrivateLoweredMap. src/js_parser/lower/lower_decorators.rs22-42Sources: src/js_parser/lower/lower_decorators.rs161-166 src/js_parser/lower/lower_decorators.rs176-186
Macros are expanded during the visit pass. When the visitor encounters a call expression that resolves to a macro, it executes the macro and replaces the call with a Substitution.
MacroJSCtx in the JavaScriptCore integration.Bun.Transpiler, which must be handled carefully to avoid freeing shared printer resources. test/regression/issue/03830.test.ts40-44Sources: test/bundler/transpiler/transpiler.test.js15-21 test/regression/issue/03830.test.ts46-54
The AST is stored in a bun_alloc::Arena (a bump allocator). Lists of nodes are represented as BumpVec or StmtNodeList, which are slices pointing directly into arena-allocated memory. This allows for extremely fast allocation and bulk deallocation of the entire AST once the transpilation of a file is complete.
Sources: src/js_parser/p.rs8-10 src/js_parser/p.rs42-44 src/js_parser/visit/visit_stmt.rs29-35
Refresh this wiki
This wiki was recently refreshed. Please wait 7 days to refresh again.