# How to Implement Custom Transformations Using SWC's Visitor and Fold Patterns

> Learn to implement custom transformations in SWC using the Visitor and Fold patterns. Mutate or produce new AST nodes with generated traits for powerful code manipulation.

- Repository: [swc/swc](https://github.com/swc-project/swc)
- Tags: how-to-guide
- Published: 2026-06-15

---

**SWC provides two complementary AST traversal patterns—`VisitMut` for in-place mutations and `Fold` for producing new nodes—that let you transform JavaScript/TypeScript code by implementing traits generated for every AST node type.**

The SWC compiler represents JavaScript and TypeScript code as a rich abstract syntax tree (AST). To manipulate this tree, the library offers two distinct traversal mechanisms defined in [`crates/swc_ecma_visit/src/generated.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_ecma_visit/src/generated.rs): the **Visitor** pattern for read-only inspection or in-place mutation, and the **Fold** pattern for creating entirely new AST nodes. These traits are automatically generated for every node type in the `swc_ecma_ast` crate, providing zero-cost abstractions for custom transformations.

## Understanding SWC's AST Traversal Architecture

SWC's transformation system relies on code-generated traits that handle the boilerplate of tree walking, allowing you to focus only on the specific nodes you want to transform.

### The Visitor Pattern (VisitMut)

The **Visitor** pattern is designed for read-only analysis or in-place mutation of existing nodes. Defined at line 7 of [`crates/swc_ecma_visit/src/generated.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_ecma_visit/src/generated.rs), the `Visit` trait provides read-only access, while `VisitMut` (defined in the same file) allows mutation of nodes in place.

The default implementation recursively traverses all children, calling the appropriate hook method for each node type. When you implement `VisitMut`, you override only the methods for node types you care about, leaving the recursive traversal to the generated boilerplate.

### The Fold Pattern

The **Fold** pattern, defined at line 111 of [`crates/swc_ecma_visit/src/generated.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_ecma_visit/src/generated.rs), is designed for transformations that replace nodes entirely. Unlike `VisitMut`, which mutates nodes in place, `Fold` methods take ownership of a node and return a potentially different node of the same type.

This pattern is ideal when you need to drop nodes, replace them with different node types, or construct new AST structures (such as converting JSX elements into function calls).

### When to Use Each Pattern

- **Use `VisitMut`** when you need to modify properties of existing nodes without changing their location in the tree (e.g., renaming identifiers, adding annotations).
- **Use `Fold`** when you need to replace, remove, or drastically restructure nodes (e.g., desugaring syntax, inlining constants).

## Implementing In-Place Mutations with VisitMut

For transformations that modify existing nodes, implement the `VisitMut` trait. The trait is automatically generated for every AST node, with default implementations that recursively visit children.

The following example demonstrates a custom visitor that renames all identifiers matching a specific pattern:

```rust
use swc_common::{errors::Handler, SourceMap, FileName, sync::Lrc};
use swc_ecma_ast::*;
use swc_ecma_parser::{Parser, StringInput, Syntax, EsConfig};
use swc_ecma_visit::{VisitMut, VisitMutWith};

/// Visitor that renames every identifier named `old` to `new_name`.
struct Renamer {
    old: JsWord,
    new_name: JsWord,
}

impl VisitMut for Renamer {
    fn visit_mut_ident(&mut self, ident: &mut Ident) {
        if ident.sym == self.old {
            ident.sym = self.new_name.clone();
        }
        // Continue walking into possible child nodes (none for Ident)
        ident.visit_mut_children_with(self);
    }
}

fn main() {
    // 1️⃣ Parse some source.
    let cm: Lrc<SourceMap> = Default::default();
    let handler = Handler::with_tty_emitter(swc_common::errors::ColorConfig::Auto, true, false, Some(cm.clone()));
    let src = r#"function foo(old) { console.log(old); }"#;
    let mut parser = Parser::new(
        Syntax::Es(EsConfig { ..Default::default() }),
        StringInput::new(src, 0, 0),
        None,
    );
    let mut module = parser.parse_module().expect("failed to parse");

    // 2️⃣ Run the visitor.
    let mut renamer = Renamer {
        old: "old".into(),
        new_name: "renamed".into(),
    };
    module.visit_mut_with(&mut renamer);

    // 3️⃣ Emit the transformed code (using the builtin printer).
    use swc_ecma_codegen::{Emitter, text_writer::JsWriter};
    let mut buf = vec![];
    {
        let mut emitter = Emitter {
            cfg: swc_ecma_codegen::Config { ..Default::default() },
            cm: cm.clone(),
            comments: None,
            wr: JsWriter::new(cm.clone(), "\n", &mut buf, None),
        };
        emitter.emit_module(&module).unwrap();
    }
    println!("{}", String::from_utf8(buf).unwrap());
}

```

**Key implementation details:**

- Override only the specific node methods you need (e.g., `visit_mut_ident`).
- Always call `visit_mut_children_with(self)` to ensure nested structures are traversed.
- Invoke the transformation using `module.visit_mut_with(&mut renamer)`, a thin wrapper generated by the trait.

## Creating New Nodes with Fold

When you need to replace nodes entirely or return different node types, use the `Fold` trait. This pattern consumes nodes and produces new ones, making it suitable for desugaring and structural transformations.

The following example replaces specific string literals with new values:

```rust
use swc_common::{SourceMap, FileName, sync::Lrc};
use swc_ecma_ast::*;
use swc_ecma_parser::{Parser, StringInput, Syntax, EsConfig};
use swc_ecma_visit::{Fold, FoldWith};

/// Fold that turns every string literal `"foo"` into `"bar"`.
struct StringReplacer;

impl Fold for StringReplacer {
    fn fold_str(&mut self, mut s: Str) -> Str {
        if s.value == "foo".into() {
            s.value = "bar".into();
        }
        // Recursively fold children (none for Str, but required by the macro)
        s.fold_children_with(self)
    }
}

fn main() {
    // Parse source.
    let cm: Lrc<SourceMap> = Default::default();
    let src = r#"const x = "foo";"#;
    let mut parser = Parser::new(
        Syntax::Es(EsConfig { ..Default::default() }),
        StringInput::new(src, 0, 0),
        None,
    );
    let module = parser.parse_module().expect("parse");

    // Apply the fold.
    let mut replacer = StringReplacer;
    let new_module = module.fold_with(&mut replacer);

    // Print result.
    use swc_ecma_codegen::{Emitter, text_writer::JsWriter};
    let mut buf = vec![];
    {
        let mut emitter = Emitter {
            cfg: swc_ecma_codegen::Config { ..Default::default() },
            cm: cm.clone(),
            comments: None,
            wr: JsWriter::new(cm.clone(), "\n", &mut buf, None),
        };
        emitter.emit_module(&new_module).unwrap();
    }
    println!("{}", String::from_utf8(buf).unwrap());
}

```

**Critical differences from VisitMut:**

- Methods return the node type (e.g., `fn fold_str(&mut self, s: Str) -> Str`) rather than accepting mutable references.
- Call `fold_children_with(self)` to continue traversal.
- The transformation returns a new value: `let new_module = module.fold_with(&mut replacer)`.

## How the Traits Are Generated

The visitor and fold traits are not hand-written for each AST node. Instead, they are automatically generated by the code generator located at [`tools/generate-code/src/generators/visitor.rs`](https://github.com/swc-project/swc/blob/main/tools/generate-code/src/generators/visitor.rs). This generator reads the AST definitions in `crates/swc_ecma_ast` and emits the traversal logic in [`crates/swc_ecma_visit/src/generated.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_ecma_visit/src/generated.rs).

The generated code ensures that every field of every node is visited in the correct order. The `VisitMut` implementation delegates to `VisitMutWith`, while `Fold` uses `FoldWith::fold_children_with`, providing the entry points for the traversal logic.

## Summary

- **SWC's visitor and fold patterns** provide type-safe, zero-cost abstractions for AST transformations in Rust.
- **Use `VisitMut`** (defined in [`crates/swc_ecma_visit/src/generated.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_ecma_visit/src/generated.rs) at line 7) for in-place mutations like renaming identifiers or adding metadata.
- **Use `Fold`** (defined at line 111 of the same file) when you need to replace nodes entirely or change AST structure.
- **Both traits are auto-generated** for every AST node by [`tools/generate-code/src/generators/visitor.rs`](https://github.com/swc-project/swc/blob/main/tools/generate-code/src/generators/visitor.rs), ensuring complete coverage of the JavaScript/TypeScript grammar.
- **Always call the children traversal methods** (`visit_mut_children_with` or `fold_children_with`) to ensure nested nodes are processed.
- **Entry points** are `module.visit_mut_with(&mut visitor)` for mutation and `module.fold_with(&mut folder)` for replacement.

## Frequently Asked Questions

### When should I use `Fold` instead of `VisitMut`?

Use **`Fold`** when you need to replace a node with a different node, remove nodes from the tree, or change the AST structure (such as converting arrow functions to regular functions). Use **`VisitMut`** when you only need to modify properties of existing nodes in place, such as renaming identifiers or toggling flags on nodes. According to the SWC source code, `Fold` produces new nodes while `VisitMut` modifies existing ones.

### How are the visitor traits generated for all AST nodes?

The traits are generated by the code generator in [`tools/generate-code/src/generators/visitor.rs`](https://github.com/swc-project/swc/blob/main/tools/generate-code/src/generators/visitor.rs). This tool reads the AST definitions from `crates/swc_ecma_ast` and outputs the complete visitor and fold implementations to [`crates/swc_ecma_visit/src/generated.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_ecma_visit/src/generated.rs). The generator creates the `Visit`, `VisitMut`, and `Fold` trait definitions along with the `visit_mut_children_with` and `fold_children_with` helper functions for every node type.

### Can I combine `VisitMut` and `Fold` in the same transformation pipeline?

Yes. The two patterns interoperate cleanly. You can implement a `Fold` that internally creates a mutable visitor to inspect or modify specific nodes during the folding process, or use a visitor to gather information before running a fold pass. The `swc_ecma_visit` crate re-exports both traits to facilitate this composition.

### Do these patterns work with TypeScript-specific AST nodes?

Yes. The code generator creates visitor and fold implementations for the entire AST, including TypeScript-specific nodes like `TsMethodSignature` or `TsTypeAnnotation`. When you parse TypeScript source using `swc_ecma_parser` with TypeScript syntax enabled, the resulting AST can be traversed using the same `VisitMut` and `Fold` traits, as they are implemented for all node types in the unified AST definition.