跪拜 Guibai
← All articles
Webpack · Frontend Engineering · Turbopack

Rspack 2.0 Ships Pure ESM Core, 1.4-Second Cached Builds, and a Single Dependency

By 程序员蜡笔熊 ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

A Rust-based bundler that runs most webpack configs with 95% API compatibility now builds 10,000-component projects in 1.4 seconds and ships with one dependency. For teams stuck on webpack, the migration cost just dropped again; for anyone auditing supply-chain risk, a single-dependency dev server resets the threat model.

Summary

Rspack 2.0 moves its core packages to pure ESM, cutting the dev-server's install footprint from 15MB to 1.4MB by bundling transitive dependencies and replacing Express with a minimal Connect-based server. A 10,000-component React benchmark clocks a cached production build at 1.4 seconds, roughly 20–25x faster than a comparable webpack 5 cached build. Persistent caching now reuses SWC minifier results, and memory usage drops over 20% when caching is on.

Tree shaking extends to CommonJS require destructuring, a new `#__NO_SIDE_EFFECTS__` annotation lets bundlers strip entire cross-module function calls when return values go unused, and Module Federation shared dependencies can be tree-shaken via runtime inference or manual export lists. Experimental React Server Components support covers `"use client"`/`"use server"` directives, compile-time RSC API checks, and HMR for both server and client components.

Breaking changes include a stricter `resolve.exportsPresence` default that errors on missing exports, removal of `module.unsafeCache`, and a requirement for Node.js 20.19+ or 22.12+. The team positions 2.0 as a deliberate departure from webpack parity—ESM-first defaults, RSC pipelines, and simplified configuration that webpack itself won't pursue.

Takeaways
Rspack 2.0 core packages are pure ESM; CommonJS projects can still `require()` them on Node.js 20.19+ or 22.12+ unless a transitive ESM dependency uses top-level await.
`@rspack/dev-server` dependencies fell from 192 to 1, and install size shrank from 15MB to 1.4MB, by bundling transitive deps and swapping Express for `connect-next`.
A 10,000-component React project builds in 3.1s without cache and 1.4s with persistent cache on an M3 Pro, roughly 20–25x faster than webpack 5 cached builds.
SWC minifier results are now cached; cache-hit builds gain roughly 50% more speed, and memory usage drops over 20% with caching enabled.
CommonJS `require` destructuring (`const { bar } = require('./foo')`) is now tree-shakeable, provided the exports object is statically analyzable.
The `#__NO_SIDE_EFFECTS__` annotation marks an entire function as side-effect-free, letting Rspack delete cross-module calls whose return values are unused.
Module Federation shared dependencies support tree shaking via `runtime-infer` mode or a manual `usedExports` list.
Experimental RSC support handles `"use client"`/`"use server"` directives, compile-time API checks, CSS collection, and dual-environment HMR.
`resolve.exportsPresence` now defaults to `error`, which can break builds that depend on libraries with sloppy `exports` fields; set it to `warn` to downgrade.
`builtin:swc-loader` no longer reads `.swcrc`; the new `detectSyntax: 'auto'` option infers JS/JSX/TS/TSX from file extensions, collapsing multiple rules into one.
`output.path` must be an absolute path in ESM config files; `__dirname` is not available by default and requires `fileURLToPath(import.meta.url)`.
CI pipelines that don't persist `node_modules/.cache` will see near-zero cache hit rates; fix the `RSPACK_CONFIG_CACHE_KEY` env var or persist the cache directory.
Conclusions

Rspack 2.0 is the first major Rust bundler to treat webpack compatibility as a floor, not a ceiling—it keeps the migration path open while adding features (pure ESM, RSC, bundler annotations) that webpack is architecturally unlikely to adopt.

The dependency count drop from 192 to 1 matters more than the performance numbers for supply-chain security. A single-dependency dev server eliminates entire classes of transitive compromise, though the vendorization strategy means security teams can't patch transitive CVEs independently and must wait for an official release.

Persistent caching in CI is a trap if not configured deliberately. The cache key depends on `package-lock.json` and environment variables; ephemeral CI runners that don't persist `node_modules/.cache` will pay a disk-I/O penalty with zero hit-rate benefit.

The `#__NO_SIDE_EFFECTS__` annotation shifts tree-shaking power from call-site pragmas to declaration-site semantics. This is a cleaner model than `/*#__PURE__*/` because it lets the bundler reason about a function globally rather than requiring developers to annotate every call site.

Rspack's RSC support is deliberately scoped to the build pipeline, not the runtime. By leaving Streaming SSR and Server Actions to frameworks, the team avoids overcommitting to a moving spec while still enabling Modern.js and TanStack Start to ship RSC on Rspack today.

Concepts & terms
Persistent Cache
A build cache that survives across process restarts by writing to disk (typically `node_modules/.cache`). Rspack 2.0 uses `package-lock.json` and environment variables to generate cache keys; without persisting the cache directory in CI, hit rates drop to zero.
#__NO_SIDE_EFFECTS__
A bundler annotation placed on a function declaration that marks the entire function as free of side effects. When the return value of a call to such a function is unused, the bundler can safely remove the entire call, even across module boundaries. Contrasts with `/*#__PURE__*/`, which marks a single call site.
require(esm)
Node.js support for loading ECMAScript modules via `require()` in CommonJS code. Stable in Node.js 22.12+, experimental in 20.19+. Fails with `ERR_REQUIRE_ASYNC_MODULE` if the ESM module uses top-level await.
Vendorization (in dependency management)
The practice of bundling transitive dependencies directly into a published npm package rather than declaring them as `dependencies`. Reduces install size and supply-chain surface but prevents consumers from patching those dependencies independently via `npm overrides`.
detectSyntax: 'auto'
A new Rspack 2.0 SWC loader option that automatically infers whether a file is JS, JSX, TS, or TSX based on its extension, eliminating the need for separate loader rules and manual `jsc.parser.syntax` configuration per file type.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗