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-serverlayer 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.jsonentry.
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/, implementVisitMut, and add it to the chain inchain_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_*.rsfile 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.