From f45ce26ac35316e355740699841b9ed3878128a6 Mon Sep 17 00:00:00 2001 From: Daniel Williams Date: Sat, 4 Jul 2026 00:44:24 +0100 Subject: [PATCH] docs: add rspack 2 dual-version support design doc Introduce agent_context/ - technical design documentation for substantial efforts, one folder per effort - with the design of dual Rspack 1.x/2.x support: version detection, Node compatibility handling, per-major config generation, persistent cache handling, React Refresh wiring, tracing, Module Federation v1, type accommodations, and the testing strategy. --- AGENTS.md | 7 ++ agent_context/README.md | 11 ++ agent_context/rspackv2-jul2026/design.md | 136 +++++++++++++++++++++++ 3 files changed, 154 insertions(+) create mode 100644 agent_context/README.md create mode 100644 agent_context/rspackv2-jul2026/design.md diff --git a/AGENTS.md b/AGENTS.md index 93227d931..afc7276ab 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -31,3 +31,10 @@ Re.Pack is a toolkit for building and developing React Native applications with - `tests/integration/`: Integration-level automated coverage. - `tests/metro-compat/`: Metro compatibility behavior coverage. - `tests/resolver-cases/`: Resolver behavior and edge-case coverage. + +## Agent Context + +- `agent_context/` contains technical design documentation for substantial + efforts (one folder per effort, e.g. `rspackv2-jul2026`). Consult it before + working on a related area, and keep it in sync when the implementation + changes. Index in `agent_context/README.md`. diff --git a/agent_context/README.md b/agent_context/README.md new file mode 100644 index 000000000..35109711a --- /dev/null +++ b/agent_context/README.md @@ -0,0 +1,11 @@ +# Agent Context + +Technical design documentation for substantial efforts in this repository — +one folder per effort. Each doc describes what the solution is and how it +works, and is kept in sync with the implementation as it evolves. + +## Index + +| Folder | Topic | +| --- | --- | +| [rspackv2-jul2026](./rspackv2-jul2026/design.md) | Dual Rspack 1.x/2.x support | diff --git a/agent_context/rspackv2-jul2026/design.md b/agent_context/rspackv2-jul2026/design.md new file mode 100644 index 000000000..9df52a399 --- /dev/null +++ b/agent_context/rspackv2-jul2026/design.md @@ -0,0 +1,136 @@ +# Dual Rspack 1.x/2.x Support — Technical Design + +A single `@callstack/repack` release supports both `@rspack/core` majors. +The source **compiles against Rspack 2 types** and **runs against both +majors**, branching at runtime on the installed major. Behavior for +Rspack 1 and webpack users is unchanged. + +## Version detection + +`src/helpers/rspackVersion.ts` resolves `@rspack/core/package.json` instead +of importing the package: + +- `@rspack/core@2` is pure ESM — a CJS `require('@rspack/core')` throws + `ERR_REQUIRE_ESM` on Node < 20.19, so detection must not load the package; +- `@rspack/core` is an optional peer dependency and may be absent entirely + (webpack-only projects). + +`getRspackVersion` / `getRspackMajorVersion` / `isRspack2` take an optional +project-context directory for resolution. `getRspackMajorVersionFromCompiler` +serves plugin contexts via `compiler.webpack.rspackVersion` (returns `null` +for webpack compilers). + +`@rspack/core` is otherwise loaded with a plain `import` — the published +package carries no workspace- or monorepo-aware resolution logic. + +## Node compatibility + +Rspack 2 requires Node `^20.19.0 || >=22.12.0`; Re.Pack's `engines` stays +`>=18` because Rspack 1 and webpack setups are unaffected. Instead of a hard +floor, `commands/rspack/ensureNodeCompat.ts` raises a clear error when +Rspack 2 is detected on an unsupported Node. The rspack commands are +lazy-loaded inside the command handlers so the guard runs before anything +touches `@rspack/core`. + +Re.Pack's compiled CJS keeps working against the ESM-only core on supported +Node versions via `require(esm)`: Rspack 2 uses the `module.exports` +ESM-interop convention, so CJS consumers receive the callable `rspack` +function with all named exports attached (`core.rspack === core`) — a shape +identical to v1. + +## Config generation + +- `experiments.parallelLoader` is emitted only under Rspack 1. Rspack 2 + removed the global flag (parallel loading is stable and opt-in per rule + via `use[].parallel`), so the loader's parallel-mode warning probe is + also skipped under v2 — running non-parallel there is a valid choice, + not a misconfiguration. +- `module.parser.javascript.exportsPresence` is set to `'auto'` under + Rspack 2. The upstream default changed from `'warn'` to `'error'`, which + breaks builds on technically-invalid imports inside `node_modules` — a + common occurrence in the React Native ecosystem that Metro tolerates. + Users can override this in their project config. + +## Persistent cache + +Rspack 2 moved the persistent cache configuration from `experiments.cache` +to top-level `cache` (same shape) and **silently ignores** the legacy key. + +- `getRspackCacheConfig` reads both locations, so `--reset-cache` works + against configs written for either major. +- Under Rspack 2 with `experiments.cache` set, `warnLegacyRspackCacheConfig` + emits a one-time warning pointing at the top-level option. The config is + left untouched — migration is the user's action. + +## React Refresh + +`DevelopmentPlugin` branches on the compiler's major: + +- **Rspack ≥ 2**: applies the official `@rspack/plugin-react-refresh@^2` + (an optional peer dependency — installed by v2 users alongside + `@rspack/core@2`) with `injectEntry: false`, `forceEnable: true`, and + `reactRefreshLoader: '@callstack/repack/react-refresh-loader'`. The + package is ESM-only with a named export, so it is loaded lazily inside + the version branch. +- **Rspack 1 and webpack**: manual wiring against client runtime files + vendored at `packages/repack/vendor/react-refresh/` (adapted from + upstream plugin v2.0.2, MIT, with a LICENSE/provenance file). The + `vendor/` directory ships as-is via `package.json#files` and is excluded + from linting and the babel build. There is no dependency on + `@rspack/plugin-react-refresh@1`. + +## Tracing / profiling + +Published Rspack 2 binaries do not include the perfetto trace layer, so +`--trace-*` profiling defaults to the `'logger'` layer under v2 +(`commands/rspack/profile/profile-2.ts`); Rspack 1 behavior is unchanged. + +## Module Federation v1 + +Under Rspack 2, `@module-federation/runtime-tools` is no longer installed +automatically by `@rspack/core`. `ModuleFederationPluginV1.apply` verifies +it is resolvable and raises an actionable error otherwise. + +## Types + +The `@rspack/core` devDependency is `^2`, so type-only imports check against +v2 while the runtime object may be either major. Notable v2 type +accommodations: + +- `SwcLoaderOptions` became a union discriminated on `detectSyntax`, which + cannot be spread-and-reassembled; internal helpers operate on a local + non-union `SwcConfig` alias (Re.Pack never sets `detectSyntax`). +- `builtin:swc-loader`-only options (`rspackExperiments`, `transformImport`, + `collectTypeScriptInfo`, `detectSyntax`) are split off before calling the + raw SWC `transformSync` API. +- One documented cast in `commands/rspack/start.ts`: Re.Pack's `devServer` + type augmentation and Rspack 2's bundled `DevServer` type are structurally + incompatible solely because each pulls `proxy` types from a different copy + of http-proxy-middleware. `bundle.ts` avoids the cast by destructuring + `devServer` off (unused when bundling). + +## Testing + +- **Unit tests run under both majors.** Jest's sandboxed CJS runtime cannot + load the ESM-only v2 core, so a custom test environment + (`jest.environment.js`) loads it outside the sandbox and a + `moduleNameMapper` bridge exposes it to suites. The environment is + parameterized on `RSPACK_MAJOR`: v2 via `await import('@rspack/core')`, + v1 via `require` of the aliased `@rspack/core-v1` devDependency. + `pnpm test:rspack1` runs the v1 lane; a lane-guard test asserts the loaded + major matches the requested one. +- **Built-dist smoke suite** (`tests/rspack-compat`) asserts the behavior + matrix above against the packed dist under both majors — the failure modes + specific to this design (require(esm) interop, loader resolution, refresh + runtime source selection) only reproduce against the built output in a + real project layout. +- **Tester apps**: `apps/tester-app` runs Rspack 2 (full feature surface). + `apps/tester-app-rspack1` is a standalone app *outside* the pnpm + workspace with Re.Pack installed from a packed tarball — required because + in-workspace apps always resolve Re.Pack's own `@rspack/core` devDependency + (v2) regardless of their manifest pin, and the published package + deliberately carries no workspace-aware resolution. +- **CI**: the unit suite runs under both majors on Linux and Windows (the + require(esm) loading path and version-helper filesystem resolution are + Windows-sensitive); Node 18/20 lanes run the Rspack 1 lane only, per the + v2 Node floor.