Open-Source Wikis

/

Next.js

/

Crates

/

next-custom-transforms

vercel/next.js

next-custom-transforms

Purpose

next-custom-transforms is the SWC visitor library for Next.js-specific JavaScript/TypeScript compiler passes. It implements the framework's special compile-time behavior: server actions, the 'use client' / 'use server' directives, the next/font import rewriting, dynamic-import tracking, page-export validation, and the React Server Components layer enforcement.

Source: crates/next-custom-transforms/src/. The biggest file in the crate (and one of the biggest in the repo) is transforms/server_actions.rs at 162,962 lines of generated/templated Rust — the hairiest single feature compile-wise.

Design

graph TD
    Source[user .ts/.tsx] --> Lexer[swc_ecma_parser]
    Lexer --> Ast[AST]
    Ast --> Pass1[next-custom-transforms passes]
    Pass1 --> Ast2[transformed AST]
    Ast2 --> Codegen[swc_ecma_codegen]
    Codegen --> Js[output js]

    Pass1 --> RSC[react_server_components<br/>50 KB]
    Pass1 --> Actions[server_actions<br/>163 KB]
    Pass1 --> StripExports[strip_page_exports<br/>35 KB]
    Pass1 --> Dynamic[dynamic / track_dynamic_imports]
    Pass1 --> Font[fonts/]

Each pass is a separate VisitMut implementation under transforms/. They are composed via a chain in chain_transforms.rs (16 KB), which the next-napi-bindings consumer invokes.

The major passes

react_server_components.rs (50 KB)

Enforces the React Server Components layer:

  • A file with 'use client' becomes a client module — no server-only imports allowed.
  • A file in the react-server layer cannot import client-only React APIs (useState, useEffect, etc.).
  • Mismatched imports become compile errors with helpful messages.
  • The pass also inserts the right import shims at the layer boundary.

server_actions.rs (163 KB)

The largest single transform. Recognizes 'use server' declarations and:

  • Hoists action bodies into server-only modules.
  • Generates stable hashed IDs for each action.
  • Creates client-side stubs that POST to the page URL with Next-Action: <id>.
  • Tracks closed-over variables ("bound arguments") and ensures they get encrypted in transit.
  • Records every action in a server-reference-manifest.json entry.

The size is mostly because the transform handles every JavaScript construct that can carry an action: arrow functions, named functions, object methods, decorators, exports, re-exports, default vs. named, anonymous vs. named bound, etc.

strip_page_exports.rs (35 KB)

Detects and validates page exports (getServerSideProps, getStaticProps, getStaticPaths, metadata, generateStaticParams, etc.) and strips them from the client bundle. The logic:

  • Trace the dependency tree from the export back through the file.
  • Mark every reachable variable, type, and helper as "server-only".
  • Emit only the rest in the client bundle.

dynamic.rs (22 KB)

Rewrites next/dynamic imports to add the right loadable manifest entries and split-point hints. Coordinates with track_dynamic_imports.rs to record each dynamic import in the loadable manifest.

fonts/ directory

Implements the next/font/google and next/font/local rewrites. When the user writes:

import { Inter } from 'next/font/google';

const inter = Inter({ subsets: ['latin'] });

The transform rewrites this into a call to a generated loader that the build executes to download the font files.

optimize_barrel.rs (15 KB)

Rewrites barrel-file imports (import { a, b, c } from 'lib') into direct imports (import a from 'lib/a'; import b from 'lib/b'; ...) when the imported library opts into per-export modules. Avoids pulling the whole barrel into the client bundle.

optimize_server_react.rs (8 KB)

A small set of React-specific tree-shaking optimizations for server components, e.g., dropping useState calls in code that never executes them.

next_ssg.rs (20 KB)

Static-analysis pass for Pages Router static-site-generation: detects getStaticProps / getStaticPaths and produces metadata used by the build.

cjs_optimizer.rs (9 KB)

Rewrites CommonJS imports of named exports (const { x } = require('lib')) into single-export form (const x = require('lib/x')) when the library's manifest allows.

warn_for_edge_runtime.rs (11 KB)

Lints user code that uses Node-only APIs in edge files. Emits warnings during compile so users know their middleware will fail at runtime.

track_dynamic_imports.rs (8 KB)

Records every next/dynamic and import() call seen in user code. Output drives the loadable manifest.

Other passes

File Role
cjs_finder.rs Detects CJS modules
debug_fn_name.rs Names anonymous functions for stack traces
debug_instant_stack.rs Adds source-mapped stack capture
disallow_re_export_all_in_page.rs Rejects export * from in pages
import_analyzer.rs Helper for analyzing imports
lint_codemod_comments.rs Lints leftover codemod comments
middleware_dynamic.rs Edge middleware-specific dynamic transforms
named_import_transform.rs Renames imports for SWC compat
page_config.rs Reads export const config for the Pages Router
pure.rs Marks calls as pure for DCE
shake_exports.rs Tree-shakes unused exports

React compiler integration

react_compiler.rs (6 KB) wires the React Compiler (formerly React Forget) into the SWC pipeline. When enabled, the compiler does automatic memoization of components.

Linter

linter.rs is a tiny stub. Most lint-like behavior lives in the warn passes (warn_for_edge_runtime.rs, lint_codemod_comments.rs).

Chain transforms

chain_transforms.rs composes the passes in the right order for each project layer (server, client, edge, react-server). The order matters: e.g., server_actions must run before strip_page_exports.

Integration points

  • Compiled into crates/next-napi-bindings/ and exposed as @next/swc.
  • Visited inside the Turbopack module options produced by crates/next-core.
  • Used directly by webpack via next-swc-loader.

Entry points for modification

  • To add a new SWC pass: create a new file under transforms/, implement VisitMut, and add it to the chain in chain_transforms.rs.
  • To change a directive's compile behavior: the pass for that directive (e.g., server_actions.rs).
  • To add a new warning: a new warn_for_*.rs file plus chain registration.

Key source files

File Purpose
crates/next-custom-transforms/src/lib.rs Crate entry
crates/next-custom-transforms/src/chain_transforms.rs Composition of passes
crates/next-custom-transforms/src/transforms/react_server_components.rs RSC layer enforcement
crates/next-custom-transforms/src/transforms/server_actions.rs Server-action compilation
crates/next-custom-transforms/src/transforms/strip_page_exports.rs Page export stripping
crates/next-custom-transforms/src/transforms/dynamic.rs next/dynamic rewriting
crates/next-custom-transforms/src/transforms/fonts/ next/font rewriting
crates/next-custom-transforms/src/transforms/optimize_barrel.rs Barrel-file optimization
crates/next-custom-transforms/src/transforms/warn_for_edge_runtime.rs Edge-runtime lint
crates/next-custom-transforms/src/react_compiler.rs React Compiler integration

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

next-custom-transforms – Next.js wiki | Factory