# SWC's AST Structure and Node Types: A Complete Guide for Tooling

> Explore SWC's AST structure and node types. Discover how its enum-based design in swc_ecma_ast ensures compile-time safety for transforming code with visitor patterns.

- Repository: [swc/swc](https://github.com/swc-project/swc)
- Tags: deep-dive
- Published: 2026-06-15

---

**SWC's AST is a strongly-typed, enum-based tree defined in the `swc_ecma_ast` crate where every syntax element—from statements to expressions—exists as a distinct enum variant, enabling compile-time safe traversal and transformation via visitor patterns.**

SWC's Abstract Syntax Tree (AST) serves as the foundation for the compiler's JavaScript and TypeScript transformations. Defined primarily in the `swc_ecma_ast` crate within the swc-project/swc repository, this tree uses Rust enums to represent every syntactic construct, providing tooling authors with zero-cost abstractions and exhaustive pattern matching against the **SWC's AST structure**.

## Core Node Types in SWC's AST Structure

### Program and ModuleItem: The Root Hierarchy

The **AST structure** begins with the `Program` enum defined in [`crates/swc_ecma_ast/src/module.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_ecma_ast/src/module.rs) at line 11. This top-level container holds either a `Script` or a `Module`, representing the two execution contexts in JavaScript. Immediately below, the `ModuleItem` enum (line 108) represents items that can appear at the top level of a module, with variants for `Stmt` (statements) or `ModuleDecl` (module declarations like imports and exports).

### Statements and Declarations

The `Stmt` enum in [`crates/swc_ecma_ast/src/stmt.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_ecma_ast/src/stmt.rs) (line 39) defines over 40 variants covering all possible statements, including `Block`, `Empty`, `Debugger`, and `Expr`. For declarations, the `Decl` enum in [`crates/swc_ecma_ast/src/decl.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_ecma_ast/src/decl.rs) (line 18) provides variants such as `FnDecl`, `ClassDecl`, and `VarDecl`, alongside TypeScript-specific declarations.

### Expressions and Patterns

Expression nodes are defined in the `Expr` enum located in [`crates/swc_ecma_ast/src/expr.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_ecma_ast/src/expr.rs) (line 33), containing more than 80 variants for literals, binary operations, function calls, and JSX elements. For destructuring and binding patterns, the `Pat` enum in [`crates/swc_ecma_ast/src/pat.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_ecma_ast/src/pat.rs) (line 16) includes `ObjectPat`, `ArrayPat`, and `AssignPat`.

### TypeScript and JSX Extensions

SWC's **AST structure** provides first-class support for TypeScript through the `TsType` enum and auxiliary types in [`crates/swc_ecma_ast/src/typescript.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_ecma_ast/src/typescript.rs) (line 312), covering type literals, interfaces, and unions. JSX support is implemented in [`crates/swc_ecma_ast/src/jsx.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_ecma_ast/src/jsx.rs) (line 18) with `JSXElement`, `JSXAttr`, and `JSXExpr` enums.

### Classes, Properties, and Operators

Class definitions use the `Class` struct and `ClassMember` enum in [`crates/swc_ecma_ast/src/class.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_ecma_ast/src/class.rs) (line 73). Object properties are represented by the `Prop` enum in [`crates/swc_ecma_ast/src/prop.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_ecma_ast/src/prop.rs) (line 18), which includes `KeyValueProp`, `GetterProp`, `SetterProp`, and `MethodProp`. Operator tokens are typed as enums in [`crates/swc_ecma_ast/src/operators.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_ecma_ast/src/operators.rs) (line 18), including `BinaryOp`, `AssignOp`, `UnaryOp`, and `UpdateOp`.

## How SWC Builds the AST Structure

### The AstNode Macro and Span Generation

Every struct in the **AST structure** is annotated with `#[ast_node(...)]`, a procedural macro that automatically generates the `Span` field and implements `Clone`, `PartialEq`, and the `Visit` traits required by the visitor infrastructure. This macro ensures that every node carries source location information while reducing boilerplate code.

### Tagged Enums for Serialization

All enums in the **AST structure** carry `#[tag("NodeType")]` attributes, which SWC uses for internal AST serialization when emitting JSON for debugging. For example, the `Stmt` enum definition in [`crates/swc_ecma_ast/src/stmt.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_ecma_ast/src/stmt.rs) uses these tags to map Rust variants to standard JavaScript node types:

```rust
#[ast_node(no_clone)]
#[derive(Eq, Hash, Is, EqIgnoreSpan)]
pub enum Stmt {
    #[tag("BlockStatement")]
    Block(BlockStmt),

    #[tag("EmptyStatement")]
    Empty(EmptyStmt),

    #[tag("DebuggerStatement")]
    Debugger(DebuggerStmt),

    #[tag("ExpressionStatement")]
    Expr(ExprStmt),
}

```

This design provides **zero-cost abstraction**—matching on enums compiles to simple integer switches avoiding dynamic dispatch—while offering **static guarantees** that all variants are handled at compile time.

## Working with SWC's AST in Rust Tooling

### Parsing JavaScript into AST Nodes

The `swc_ecma_parser` crate constructs the **AST structure** from source code. The `Parser::new` function accepts a `Syntax` configuration and `StringInput` to produce a `Module` or `Script` root node:

```rust
use swc_common::{SourceMap, FileName, sync::Lrc};
use swc_ecma_parser::{Parser, StringInput, Syntax, EsConfig};

fn parse_module(src: &str) -> swc_ecma_ast::Module {
    let cm: Lrc<SourceMap> = Default::default();
    let fm = cm.new_source_file(FileName::Custom("input.js".into()), src.into());

    let mut parser = Parser::new(
        Syntax::Es(EsConfig {
            jsx: true,
            ..Default::default()
        }),
        StringInput::from(&*fm),
        None,
    );

    parser
        .parse_module()
        .expect("failed to parse module")
}

```

### Traversing with the Visit Trait

Tooling can traverse the **AST structure** using the `Visit` trait from `swc_ecma_visit`. The `visit_with` method dispatches to type-specific methods such as `visit_ident` or `visit_fn_decl`:

```rust
use swc_ecma_ast::*;
use swc_ecma_visit::{Visit, VisitWith};

struct IdentifierCollector {
    pub ids: Vec<String>,
}

impl Visit for IdentifierCollector {
    fn visit_ident(&mut self, ident: &Ident, _: &dyn Node) {
        self.ids.push(ident.sym.to_string());
    }
}

fn collect_idents(module: &Module) -> Vec<String> {
    let mut visitor = IdentifierCollector { ids: vec![] };
    module.visit_with(&mut visitor);
    visitor.ids
}

```

### Transforming with the Fold Trait

Mutable transformations use the `Fold` trait, which creates a new **AST structure** with modified nodes. The `fold_with` method applies a folder across the entire tree:

```rust
use swc_ecma_ast::*;
use swc_ecma_visit::{Fold, FoldWith};

struct VarToLet;

impl Fold for VarToLet {
    fn fold_var_decl(&mut self, mut decl: VarDecl) -> VarDecl {
        decl.kind = VarDeclKind::Let;
        decl
    }
}

fn transform(module: Module) -> Module {
    module.fold_with(&mut VarToLet)
}

```

### Serializing to JSON for Debugging

When the `serde` feature is enabled, the entire **AST structure** supports serialization. This is useful for inspecting the tree during development:

```rust
use swc_ecma_ast::*;
use serde_json::to_string_pretty;

fn ast_to_json<T: serde::Serialize>(node: &T) -> String {
    to_string_pretty(node).expect("serialization failed")
}

// Example usage:
// let module = parse_module("function foo(){ return 42; }");
// println!("{}", ast_to_json(&module));

```

## Essential Source Files for AST Manipulation

The following files in the swc-project/swc repository constitute the public surface for **SWC's AST structure**:

- [`crates/swc_ecma_ast/src/lib.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_ecma_ast/src/lib.rs) — Public re-exports and macro glue for the entire AST
- [`crates/swc_ecma_ast/src/module.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_ecma_ast/src/module.rs) — `Program`, `Module`, `Script`, and `ModuleItem` definitions
- [`crates/swc_ecma_ast/src/stmt.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_ecma_ast/src/stmt.rs) — `Stmt` enum and related statement structs
- [`crates/swc_ecma_ast/src/expr.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_ecma_ast/src/expr.rs) — `Expr` enum and expression helpers
- [`crates/swc_ecma_ast/src/decl.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_ecma_ast/src/decl.rs) — `Decl` enum for functions, classes, and variables
- [`crates/swc_ecma_ast/src/pat.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_ecma_ast/src/pat.rs) — Pattern types for destructuring
- [`crates/swc_ecma_ast/src/prop.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_ecma_ast/src/prop.rs) — Object property variants
- [`crates/swc_ecma_ast/src/jsx.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_ecma_ast/src/jsx.rs) — JSX-specific node definitions
- [`crates/swc_ecma_ast/src/typescript.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_ecma_ast/src/typescript.rs) — Full TypeScript type system nodes
- [`crates/swc_ecma_ast/src/lit.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_ecma_ast/src/lit.rs) — `Lit` enum for primitives (strings, numbers, booleans, regex, bigint)
- [`crates/swc_ecma_ast/src/class.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_ecma_ast/src/class.rs) — Class declarations and member definitions
- [`crates/swc_ecma_ast/src/operators.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_ecma_ast/src/operators.rs) — Binary, unary, and assignment operators
- [`crates/swc_ecma_parser/src/lib.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_ecma_parser/src/lib.rs) — Parser entry point that produces the AST
- [`crates/swc_visit/src/lib.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_visit/src/lib.rs) — Visitor (`Visit`) and folder (`Fold`) trait definitions

## Summary

- **SWC's AST structure** uses Rust enums to represent all JavaScript and TypeScript syntax, providing compile-time safety and zero-cost pattern matching.
- The `Program` and `ModuleItem` enums serve as the root containers, while `Stmt`, `Expr`, `Decl`, and `Pat` enums categorize specific syntax elements.
- The `#[ast_node]` macro generates common traits and span information, while `#[tag(...)]` attributes enable JSON serialization.
- Tooling interacts with the AST through the `Visit` trait (for reading) and `Fold` trait (for transforming), defined in the `swc_visit` crate.
- All AST types support `serde` serialization when the feature is enabled, facilitating debugging and interoperability.

## Frequently Asked Questions

### What makes SWC's AST structure different from Babel's?

SWC's AST uses Rust's enum system to enforce type safety at compile time, whereas Babel uses a loose object-based tree. This means SWC visitors must handle all enum variants, preventing runtime errors, and pattern matching optimizes to integer switches rather than property lookups.

### How does pattern matching work on SWC AST nodes?

Pattern matching uses Rust's `match` expressions on the enum variants. For example, matching on a `Stmt` can destructure it into specific variants like `Stmt::Block(block)` or `Stmt::Expr(expr)`, allowing exhaustive handling of all 40+ statement types defined in [`stmt.rs`](https://github.com/swc-project/swc/blob/main/stmt.rs).

### Can I use SWC's AST for TypeScript type-checking?

While the **AST structure** includes comprehensive TypeScript nodes in [`typescript.rs`](https://github.com/swc-project/swc/blob/main/typescript.rs) (line 312), SWC itself is a transpiler, not a type checker. The AST provides the syntax tree for type annotations, but semantic type checking requires additional symbol resolution logic beyond the scope of the core AST.

### What is the performance cost of SWC's visitor pattern?

The visitor pattern incurs **zero runtime overhead** compared to manual traversal. The `Visit` and `Fold` traits use static dispatch, and enum matching compiles to direct jumps. According to the source implementation in `swc_visit`, this design avoids the virtual table lookups common in object-oriented AST implementations.