Skip to content

feat: support Rspack 2 alongside Rspack 1 - reference implementation (to be broken down)#1393

Draft
dannyhw wants to merge 24 commits into
mainfrom
feat/rspack-2-support
Draft

feat: support Rspack 2 alongside Rspack 1 - reference implementation (to be broken down)#1393
dannyhw wants to merge 24 commits into
mainfrom
feat/rspack-2-support

Conversation

@dannyhw

@dannyhw dannyhw commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds Rspack 2 support to Re.Pack while keeping Rspack 1 fully working from the same release. Re.Pack detects the installed @rspack/core major at runtime and adapts — no separate builds, no breaking changes for existing Rspack 1 / webpack users.

Full research, decisions, and verification behind this PR live in agent_context/rspackv2-jul2026/ (added on this branch): breaking-changes inventory mapped to our code, dual-version plan, decided questions, an executed V1–V11 verification checklist against @rspack/core@2.1.2, and a React Refresh deep dive.

What changed

Version detection (foundation)

  • New getRspackVersion/getRspackMajorVersion/isRspack2 helpers — resolve @rspack/core/package.json instead of importing the package, so they're safe on any Node version and when @rspack/core isn't installed.

Config generation

  • experiments.parallelLoader is only set under Rspack 1 (removed in v2 — verified silently ignored, i.e. dead config).
  • module.parser.javascript.exportsPresence: 'auto' under Rspack 2 — v2 changed the default from 'warn' to 'error', which breaks builds on invalid imports inside node_modules (common in the RN ecosystem, tolerated by Metro). Overridable in project config.

Persistent cache

  • v2 moved experiments.cache → top-level cache and silently ignores the legacy key (verified: no error, cache just off). Re.Pack now reads both locations, and under v2 auto-applies a legacy experiments.cache value to cache with a warning so users don't silently lose caching. --reset-cache works with both shapes.

React Refresh (biggest piece — see agent_context/rspackv2-jul2026/06)

  • The @rspack/plugin-react-refresh@1.0.0 dependency (and its removed deprecated_runtimePaths API) is gone.
  • Rspack 2: applies the official @rspack/plugin-react-refresh v2 plugin through its new integrator options (injectEntry: false + reactRefreshLoader: '@callstack/repack/react-refresh-loader'), keeping Re.Pack's per-entrypoint entry ordering and RN-aware loader. The plugin is a new optional peer dependency — Rspack 2 users install it alongside @rspack/core@2 (a friendly error explains this if missing). It's ESM-only, so it's required lazily inside the v2 branch only.
  • Rspack 1 + webpack: the three client runtime files are vendored into src/modules/reactRefresh/ (MIT, adapted from the v2 plugin) and wired manually exactly as before — defines updated from the removed overlay flags to __reload_on_runtime_errors__: false.

Commands & environment

  • Rspack commands are loaded lazily; a guard raises Rspack X requires Node.js ^20.19.0 || >=22.12.0 instead of an obscure ERR_REQUIRE_ESM crash (v2 is pure ESM). Bonus: loading react-native.config.js no longer touches @rspack/core at all.
  • --trace-* defaults to the logger layer under v2 (published v2 binaries are built without perfetto — verified).
  • ModuleFederationPluginV1 pre-checks @module-federation/runtime-tools under v2 (no longer auto-installed by @rspack/core) with an actionable error.

Types & tests

  • packages/repack now compiles against @rspack/core@2 types (other packages stay on the v1 catalog). Fallout handled without casts: non-union SwcConfig alias (v2 made SwcLoaderOptions a union on detectSyntax), loader-only SWC options split off before the raw transform call, cache types derived from Configuration['cache'] with the legacy experiments.cache location modeled explicitly, and devServer split off via destructuring in bundle. One irreducible cast remains in start (Re.Pack's devServer augmentation vs v2's bundled DevServer type differ only by duplicated http-proxy-middleware type trees) - documented in a comment at the site.
  • Jest can't load the ESM-only core in its sandboxed runtime — a custom test environment preloads it natively and a moduleNameMapper bridge exposes it, so all suites now run against the real Rspack 2 (280 tests, up from 234 — 4 suites previously silently type-only).

Verification

  • pnpm build / typecheck / test (280/280) / biome — green across all 12 package tasks.
  • Smoke-tested the built dist against both majors in isolated projects (Node 26):
    • Rspack 1.7.12: config keeps parallelLoader, no parser override, cache accessor + migration, Node guard, and a full dev build with HMR + React Refresh using the vendored client files ✅
    • Rspack 2.1.2: no parallelLoader, exportsPresence: 'auto', cache migration, Node guard, and a full dev build with HMR + React Refresh through the official v2 plugin (with Re.Pack's loader + entry ordering) ✅
  • Earlier lab verification (agent_context/rspackv2-jul2026/07): the full RN chunk-loading chain (load_script interception, jsonp + array-push + globalObject: 'self') executes correctly under v2; MF v1 + v2 (enhanced@2.6.0) build clean.

Not in this PR (follow-ups per plan phases 3–4)

  • CI matrix + tester-app / tests/metro-compat / tests/resolver-cases runs under v2 (needs device validation for HMR e2e)
  • Flipping repack-init, templates, and website docs to default to Rspack 2 (decided, gated on the validation pass)
  • Website migration guide for users

dannyhw added 6 commits July 2, 2026 15:07
- detect installed @rspack/core major and adapt generated config:
  experiments.parallelLoader only under v1, exportsPresence 'auto' under v2
- read persistent cache config from both v1 (experiments.cache) and v2
  (top-level cache) locations; auto-migrate legacy value under v2 with a warning
- React Refresh: apply official @rspack/plugin-react-refresh v2 (optional peer)
  under rspack 2 via its integrator options (injectEntry: false + custom loader);
  vendor the client runtime files for webpack & rspack 1 manual wiring;
  drop the @rspack/plugin-react-refresh@1 dependency
- load rspack commands lazily and fail fast with a clear error when running
  rspack 2 on Node < 20.19 (ESM-only package requires require(esm) support)
- default --trace-* to the logger layer under rspack 2 (published binaries
  lack the perfetto trace layer)
- verify @module-federation/runtime-tools is installed for MFv1 under rspack 2
- compile against @rspack/core@2 types; bridge ESM-only core into jest via
  a custom test environment
@changeset-bot

changeset-bot Bot commented Jul 2, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 68925e4

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 6 packages
Name Type
@callstack/repack Minor
@callstack/repack-plugin-expo-modules Minor
@callstack/repack-plugin-nativewind Minor
@callstack/repack-plugin-reanimated Minor
@callstack/repack-dev-server Minor
@callstack/repack-init Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Jul 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
repack-website Ready Ready Preview, Comment Jul 3, 2026 9:15pm

Request Review

- babelSwcLoader: split builtin:swc-loader-only options (rspackExperiments,
  transformImport, collectTypeScriptInfo, detectSyntax) off before calling the
  raw SWC transform API instead of casting - also more correct at runtime
- cache handling: derive RspackConfigurationWithLegacyCache from
  Configuration['cache']/(Configuration['experiments'] & legacy key) so the
  accessor and migration helper need no casts; move the migration call from
  makeCompilerConfig (weakly-typed generic flow) to start/bundle where configs
  are concretely typed
- bundle: split devServer off via rest-destructuring instead of delete + cast
- parallelLoader probe: 'in' narrowing instead of a cast
- version helper: type annotation instead of assertion on require()d JSON
- start: keep the single irreducible cast (devServer augmentation vs Rspack's
  bundled DevServer type differ only by duplicated http-proxy-middleware type
  trees) with full reasoning in a comment
Establishes agent_context/ as dated documentation of agent-assisted work -
a durable record of research, decisions, plans, and verification results.
Conventions documented in agent_context/README.md and referenced from AGENTS.md.
@dannyhw

dannyhw commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator Author

next step is going to be to split this pr into multiple prs, this branch will serve as a reference for work done until now

dannyhw added 5 commits July 3, 2026 11:41
…ng it

Per maintainer feedback on #1393: when Rspack 2 ignores a configured
experiments.cache, warn once and leave the config untouched instead of
copying the value to the top-level cache option. --reset-cache still
reads both locations via getRspackCacheConfig.
…ndor/

Per maintainer feedback on #1393: the adapted upstream files (verbatim
from @rspack/plugin-react-refresh v2.0.2 apart from headers) live under
vendor/react-refresh with a LICENSE recording provenance, are shipped
as-is via the files allowlist, and are excluded from biome so they stay
diffable against upstream. DevelopmentPlugin resolves them relative to
the package root so the path works from both src/ and dist/.
jest.environment.js loads the aliased @rspack/core-v1 devDependency when
RSPACK_MAJOR=1 (plain CJS require; v2 stays an ESM import) and exposes
__RSPACK_MAJOR__. pnpm test:rspack1 runs the suite on the real Rspack 1
core surface; a lane-guard test asserts the loaded major matches the
requested lane so a silently-ignored switch can never pass again.

Honest scope: in-workspace version detection still resolves repack's own
@rspack/core@^2 devDependency, so this lane covers the v1 core object and
compilation surface, not version routing (doc 08 § Correction).
…ck 1 lab

apps/tester-app moves to the new rspack2 named catalog and is the
Rspack 2 example. apps/tester-app-rspack1 is a standalone app excluded
from the workspace (own node_modules, repack installed from a packed
tarball via pack-repack) because in-workspace repack's @rspack/core@^2
devDependency shadows any app-level v1 pin - it is the only surface
that genuinely runs Rspack 1 (doc 08 § Discovery).

Validated on-device (Android + iOS) for both apps: HMR/React Refresh,
local + remote chunk loading, mini-app install/show/remove, all asset
frames incl. remote assets (doc 08 § On-device validation).
Doc 08 gains the devDep-shadowing discovery (with the implemented-then-
reverted loadRspack decision and the no-monorepo-leaks working
agreement), the dual-major jest lane correction, and the on-device
validation state. Doc 09 reflects the reference branch after the
reworks; docs 01/02/03/05/06/07 get dated AMENDED/superseded notes.
Doc 10 evaluates the maintainer feedback; the smoke-harness appendix
matches the warn-only cache behavior and vendored file locations.
The default catalog's v1 pin was cosmetic: every workspace consumer
already ran repack's @rspack/core@^2 devDependency (devDep shadowing,
doc 08). The default catalog now pins ^2.1.2 (+ plugin-react-refresh,
newer @swc/helpers); the rspack2 named catalog is gone, tester-app pins
plain catalog: again, and both federation apps declare the refresh
plugin like any real Rspack 2 app. The Rspack 1 surfaces stay explicit:
the standalone apps/tester-app-rspack1 lab and pnpm test:rspack1.

This put tests/integration's rspack lane (which imports @rspack/core
directly) on v2 for the first time, surfacing output-format changes,
not behavioral ones: custom runtime-module banners lost the
webpack/runtime/ prefix, unminified module factories use shorthand
method syntax, and the polyfills runtime module now precedes MFv2's
embed_federation_runtime wrapper (polyfills still execute before
__webpack_require__.x() is invoked - the invariant the tests now
assert directly). Markers made version-agnostic, an Rspack 2
module-factory regex added, rspack-lane snapshots regenerated
under 2.1.2.
Rspack 2 colorizes the diagnostics it inlines into error-stub modules
when the environment enables color, so the CI run of the rspack lane
produced bundles whose stub error strings embed escaped ANSI codes
(backslash-u001b[31m etc.) while local colorless runs do not.
normalizeBundle now strips both the raw and string-escaped forms,
making the NativeEntryPlugin snapshots environment-independent
(verified locally with and without FORCE_COLOR=3).
For @module-federation/enhanced 0.15.x/0.21.x under Rspack 2,
embed_federation_runtime inlines a data: URI module whose URL-encoded
source embeds the absolute path to webpack-bundler-runtime, so the
snapshots carried machine-specific paths (local vs CI runner).
normalizeBundle now also rewrites the encodeURIComponent form of the
repo root.
README gains a start-here section pointing at doc 09 and the open work
items; doc 08 commit map replaces the stale working-tree row with the
actual pushed commits (CI green at 55b6739) and gains the CI
snapshot-portability findings; doc 09 status header, PR 8 catalog
instructions, porting deltas, and per-branch validation commands now
match the flipped default catalog and final branch state.
@dannyhw dannyhw changed the title feat: support Rspack 2 alongside Rspack 1 feat: support Rspack 2 alongside Rspack 1 - reference implementation (to be broken down) Jul 3, 2026
dannyhw added a commit that referenced this pull request Jul 3, 2026
Introduce agent_context/ - dated documentation of substantial research and
planning efforts (conventions in agent_context/README.md) - with the full
rspack 2 adoption plan: breaking-changes inventory, impact analysis,
dual-version support plan, decisions, verification results, implementation
notes, the PR split plan, and the maintainer feedback evaluation for #1393.
dannyhw added a commit that referenced this pull request Jul 3, 2026
Introduce agent_context/ - dated documentation of substantial research and
planning efforts (conventions in agent_context/README.md) - with the full
rspack 2 adoption plan: breaking-changes inventory, impact analysis,
dual-version support plan, decisions, verification results, implementation
notes, the PR split plan, and the maintainer feedback evaluation for #1393.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant