Skip to content

Commit 360aaf6

Browse files
committed
docs: add implementation notes and PR split plan (where we left off)
1 parent 2f50d03 commit 360aaf6

4 files changed

Lines changed: 270 additions & 7 deletions

File tree

agent_context/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,4 @@ would otherwise live only in chat transcripts.
2424

2525
| Folder | Topic | Status |
2626
| --- | --- | --- |
27-
| [rspackv2-jul2026](./rspackv2-jul2026/README.md) | Rspack 2 support with dual Rspack 1/2 compatibility | Implemented (PR [#1393](https://github.com/callstack/repack/pull/1393)); follow-ups: workspace adoption + defaults flip |
27+
| [rspackv2-jul2026](./rspackv2-jul2026/README.md) | Rspack 2 support with dual Rspack 1/2 compatibility | Implemented on reference branch `feat/rspack-2-support`; being split into a PR stack (see doc 09) |
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
# Implementation Notes
2+
3+
What was actually built on `feat/rspack-2-support`, including the non-obvious
4+
technical details discovered during implementation that aren't captured in the
5+
research docs (01–07). Written so a fresh context can pick up the work without
6+
the original conversation.
7+
8+
## Commits on this branch
9+
10+
| Commit | Contents |
11+
| --- | --- |
12+
| `914d3053` | Research docs (01–05 + README) |
13+
| `8d741365` | React Refresh deep dive (doc 06) |
14+
| `7d8687f8` | Q1–Q5 decisions recorded |
15+
| `0b1a3a5a` | V1–V11 verification results (doc 07) |
16+
| `d0ea05d6` | **feat: support rspack 2 alongside rspack 1** — the core implementation |
17+
| `ba841a6a` | **refactor: replace type casts with proper typing** |
18+
| `f623076f` | docs moved to `agent_context/rspackv2-jul2026/` |
19+
| `2f50d03b` | agent_context lifecycle refinement |
20+
21+
Draft PR for the whole branch: [#1393](https://github.com/callstack/repack/pull/1393)
22+
(to be closed in favor of the PR stack — see doc 09).
23+
24+
## What was implemented (plan phases 0–2)
25+
26+
1. **Version detection**`packages/repack/src/helpers/rspackVersion.ts`:
27+
`getRspackVersion` / `getRspackMajorVersion` / `isRspack2` resolve
28+
`@rspack/core/package.json` instead of importing the package (safe on any
29+
Node version, safe when not installed); `getRspackMajorVersionFromCompiler`
30+
reads `compiler.webpack.rspackVersion` for plugin contexts.
31+
`profile/index.ts` refactored onto it.
32+
2. **Node guard + lazy commands**`commands/rspack/ensureNodeCompat.ts`
33+
raises a clear `CLIError` for Rspack 2 on Node < 20.19 (instead of
34+
`ERR_REQUIRE_ESM`). `commands/rspack/index.ts` now lazy-imports
35+
`start`/`bundle` inside the command `func`s — the guard runs first, and
36+
loading `react-native.config.js` no longer touches `@rspack/core` at all.
37+
3. **Config generation**`getRepackConfig`: `experiments.parallelLoader`
38+
only under v1; `module.parser.javascript.exportsPresence: 'auto'` under v2
39+
(decision Q1). `checkParallelModeAvailable` in babelSwcLoader skips its
40+
probe under v2 (the global flag no longer exists; non-parallel is a valid
41+
choice there, not a misconfiguration).
42+
4. **Persistent cache**`getRspackCacheConfig` reads both the v1
43+
(`experiments.cache`) and v2 (top-level `cache`) locations;
44+
`migrateLegacyRspackCacheConfig` (called from `start`/`bundle` when
45+
`isRspack2`) moves a legacy value to `cache` with a one-time warning —
46+
because v2 *silently ignores* the legacy key (verified in doc 07).
47+
5. **React Refresh** — per the doc 06 decision: `@rspack/[email protected]`
48+
dependency deleted; v2 plugin added as **optional peerDependency** (`^2.0.0`);
49+
`DevelopmentPlugin` splits on major — rspack≥2 applies the official plugin
50+
(`injectEntry: false`, `forceEnable: true`,
51+
`reactRefreshLoader: '@callstack/repack/react-refresh-loader'`, lazily
52+
`require`d inside the branch since the package is ESM-only), webpack +
53+
rspack 1 use the manual wiring pointed at client files **vendored** into
54+
`src/modules/reactRefresh/` (adapted from plugin v2.0.2, MIT; defines swap
55+
the removed overlay flags for `__reload_on_runtime_errors__: false`).
56+
6. **Tracing**`profile-2.ts` defaults the trace layer to `'logger'` under
57+
v2 (published v2 binaries lack perfetto, verified V9).
58+
7. **MFv1 pre-check**`ModuleFederationPluginV1.apply` verifies
59+
`@module-federation/runtime-tools` is resolvable under rspack≥2 with an
60+
actionable error (no longer auto-installed by `@rspack/core`).
61+
8. **Types**`packages/repack` devDeps moved to `@rspack/core@^2.1.2` +
62+
`@swc/helpers@^0.5.23` (workspace catalog stays `^1.6.0` for everything
63+
else). `ConfigKeys` gained `cache` and `experiments` (it genuinely
64+
under-described what the commands read).
65+
66+
## Non-obvious technical details (hard-won, do not rediscover)
67+
68+
### Jest cannot load ESM-only @rspack/core — sandbox escape required
69+
Jest's CJS module runtime cannot `require(esm)`, and **`createRequire` inside
70+
the Jest sandbox is wrapped by Jest** — a bridge module calling
71+
`createRequire(__filename)('@rspack/core')` loops back through
72+
`moduleNameMapper` into itself (observed: the bridge received its own partial
73+
exports). The working escape: a custom test environment
74+
(`jest.environment.js`) — environments load *outside* the sandbox with Node's
75+
real module system — preloads the core via `await import()` and exposes it as
76+
`this.global.__RSPACK_CORE__`; `jest.rspack-core-bridge.js` (mapped via
77+
`moduleNameMapper`) reshapes it with `__esModule: true` so Babel's import
78+
interop keeps named imports working. Result: all suites run against the real
79+
Rspack 2 — 280 tests (previously 234; four suites weren't even loading).
80+
81+
### require(esm) interop shape
82+
`require('@rspack/core')` under v2 returns **the callable `rspack` function
83+
itself** with all named exports attached (`core.rspack === core`) — Rspack
84+
uses Node's `module.exports` ESM-interop convention, so CJS consumers get a
85+
v1-identical shape. No `__esModule` marker, no `default` export. Babel-compiled
86+
named imports work unchanged. (`@rspack/plugin-react-refresh@2` does NOT do
87+
this — it has a **named export only**; a default import yields `undefined`.)
88+
89+
### v2 type fallout patterns
90+
- `SwcLoaderOptions` became a **union discriminated on `detectSyntax`**
91+
spread-and-override helpers can't reassemble a union. Fix: local non-union
92+
`SwcConfig` alias in `loaders/babelSwcLoader/options.ts`
93+
(`Omit<SwcLoaderOptions, 'jsc' | 'detectSyntax'> & { detectSyntax?: false; jsc?: SwcLoaderJscConfig }`).
94+
- The raw SWC `transformSync` options type doesn't accept
95+
`builtin:swc-loader`-only keys — `babelSwcLoader` now destructures
96+
`rspackExperiments`/`transformImport`/`collectTypeScriptInfo`/`detectSyntax`
97+
off before calling it (also more correct at runtime). `SwcOverrides`
98+
excludes them too.
99+
- Cache types are derived from source of truth:
100+
`RspackConfiguration['cache']` + `NonNullable<Configuration['experiments']> & { cache?: ... }`
101+
(the intersection also defeats TS's weak-type check that plagued
102+
structurally-typed attempts).
103+
- TS's **weak-type check** rejects all-optional parameter types when an
104+
argument's union members share no properties — this is why the cache
105+
migration call lives in `start`/`bundle` (concrete `Configuration[]`), not
106+
in `makeCompilerConfig` (inferred literal-union from `webpack-merge`).
107+
108+
### Working agreement: no type casts
109+
Maintainer preference (Daniel, 2026-07-02): avoid `as X` and especially
110+
`as unknown as X`. Use `satisfies`, type narrowing, or fix the underlying
111+
mismatch; where genuinely impossible, keep the cast with the full reasoning
112+
in a comment and call it out in review. Current state: **one** irreducible
113+
cast remains, in `commands/rspack/start.ts` — Re.Pack's `devServer` type
114+
augmentation (`src/types/dev-server-options.d.ts`) vs Rspack 2's bundled
115+
`DevServer` type are incompatible solely because each pulls `proxy` types from
116+
a different copy of http-proxy-middleware; `devServer` can't be stripped there
117+
because the dev-server flow reads it back from `compiler.options`. The genuine
118+
fix would be aligning `@callstack/repack-dev-server`'s proxy types with
119+
rspack's bundled ones (possible future work). `bundle.ts` avoids the cast by
120+
rest-destructuring `devServer` off (it's not needed for bundling).
121+
122+
### Misc
123+
- The `devServer` key is *accepted* by v2 at runtime (validation is loose —
124+
doc 07); the conflict is purely type-level.
125+
- Repack's `type: 'javascript/auto'` on transform rules is what makes its ESM
126+
`dist/modules/*` files bundle inside a `"type": "commonjs"` package scope —
127+
a bare config without such a rule parses them as CJS and fails.
128+
- Biome's `useOptionalChain` conflicts with TS narrowing on `false | DevServer`;
129+
`typeof x === 'object' && x.hot` satisfies both.
130+
131+
## Verification performed
132+
133+
- `pnpm typecheck` / `build` / `test` (280/280) / biome — clean;
134+
`turbo run build typecheck` green across all 12 workspace tasks.
135+
- **Smoke tests of the built dist** in isolated projects (Node 26), script
136+
kept in the session scratchpad (`smoke.cjs`, labs `v1-lab`/`v2-lab`):
137+
- Rspack **1.7.12**: parallelLoader kept, no parser override, cache
138+
accessor + migration, Node guard, full dev build with HMR + React Refresh
139+
via the **vendored files** — all PASS.
140+
- Rspack **2.1.2**: no parallelLoader, `exportsPresence: 'auto'`, cache
141+
migration, Node guard, full dev build with HMR + React Refresh via the
142+
**official v2 plugin** — all PASS.
143+
- Lab setup notes: react-native stubbed via `resolve.alias` (RN sources
144+
need the full flow/babel loader chain); loader resolved via
145+
`resolveLoader.alias`; rspack-2 users of the official plugin also need
146+
`react-refresh` installed (pnpm-strict layouts won't hoist repack's copy
147+
into the plugin's resolution scope).
148+
- NOT yet verified (phase 3): device HMR e2e, tester apps, metro-compat /
149+
resolver-cases suites under v2, CI matrix.
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# PR Split Plan & Where We Left Off
2+
3+
> **STATUS / WHERE WE LEFT OFF (2026-07-02):** All research, decisions,
4+
> implementation (plan phases 0–2), and verification are complete on branch
5+
> `feat/rspack-2-support` (see [08-implementation-notes.md](./08-implementation-notes.md)).
6+
> The next step is splitting that branch into the PR stack below, starting
7+
> with PR 1 (this documentation). **The split has not been executed yet.**
8+
>
9+
> Branch strategy (agreed): `feat/rspack-2-support` stays as a **reference
10+
> version** — kept locally and on the remote. Draft PR
11+
> [#1393](https://github.com/callstack/repack/pull/1393) will be **closed
12+
> (not merged)** once the stack PRs are open, and referenced from them.
13+
>
14+
> Maintainer feedback on #1393 is recorded below and must be incorporated
15+
> while building the stack.
16+
17+
## Maintainer feedback on #1393
18+
19+
> _To be filled in — feedback received from the PR maintainer, to incorporate
20+
> before/while creating the stack._
21+
22+
<!-- PENDING: paste maintainer feedback here -->
23+
24+
## The stack
25+
26+
Ordered by merge order; each PR is independently green (typecheck, build,
27+
tests, lint, and the dual-major smoke tests from doc 08 where applicable).
28+
29+
### PR 1 — Rspack 2 adoption plan (documentation)
30+
- Introduces `agent_context/` (convention + `README.md` index, `AGENTS.md`
31+
pointer) and `agent_context/rspackv2-jul2026/` with all documents 01–09.
32+
- No code changes. Title: "docs: rspack 2 adoption plan".
33+
34+
### PR 2 — Foundations & guards
35+
- `helpers/rspackVersion.ts` (+ helpers index export)
36+
- `commands/rspack/ensureNodeCompat.ts` + lazy command loading in
37+
`commands/rspack/index.ts`
38+
- `profile/index.ts` refactor onto the version helper
39+
- Zero behavior change for Rspack 1 / webpack users. No changeset-worthy
40+
user-visible behavior beyond the friendlier error (patch changeset at most).
41+
42+
### PR 3 — Compile against Rspack 2 types
43+
- `packages/repack` devDeps: `@rspack/core@^2.1.2`, `@swc/helpers@^0.5.23`
44+
(workspace catalog stays v1)
45+
- Type fallout: `SwcConfig` alias, transformSync loader-only-options split,
46+
`bundle.ts` devServer destructure, the single documented cast in `start.ts`,
47+
`RepackTargetPlugin` devServer narrowing, `ConfigKeys` additions
48+
- `getRspackCacheConfig` + derived cache types (**must** be here — the old
49+
`experiments.cache`-derived type doesn't compile against v2 types)
50+
- Jest custom environment + bridge (`jest.environment.js`,
51+
`jest.rspack-core-bridge.js`, `jest.config.js`)
52+
- Note for reviewers: the cache *types* land here; their runtime purpose
53+
completes in PR 4 (cross-link the PRs).
54+
55+
### PR 4 — Route renamed/moved config options
56+
- `getRepackConfig`: `parallelLoader` gating + `exportsPresence: 'auto'`
57+
- `migrateLegacyRspackCacheConfig` + wiring in `start`/`bundle`
58+
- Version-aware parallel-mode warning probe in babelSwcLoader
59+
- `profile-2.ts` (logger trace layer default under v2)
60+
- MFv1 `@module-federation/runtime-tools` pre-check
61+
- Depends on PR 2 (`isRspack2`) and PR 3 (cache types).
62+
63+
### PR 5 — React Refresh restructure
64+
- Vendored client files (`src/modules/reactRefresh/`), `DevelopmentPlugin`
65+
split, `@rspack/[email protected]` dependency → `^2` optional peer
66+
- Depends on PR 2 only (`getRspackMajorVersionFromCompiler`) — can be
67+
reviewed in parallel with PR 3/4
68+
- The riskiest PR for dev experience; needs its own manual HMR pass
69+
- The headline **minor changeset** ("Rspack 2 support") goes on whichever of
70+
PR 4 / PR 5 merges last.
71+
72+
### PR 6+ — Workspace adoption (phase 3, separate effort)
73+
- Catalog/tester-app/tests bump to v2, CI matrix lanes (Node 18/20 = v1-only),
74+
metro-compat + resolver-cases sweeps, device HMR e2e
75+
- Per the agent_context lifecycle this is follow-up work → **new dated
76+
folder** (e.g. `rspackv2-adoption-<month><year>/`) linking back here.
77+
- Also parked for later (phase 4, decided): flipping `repack-init`/templates/
78+
website defaults to Rspack 2, website migration guide.
79+
80+
## Mechanics for executing the split
81+
82+
- Cut `feat/rspack-2-01-*` (naming TBD) from `main`; materialize each PR's
83+
file set via `git checkout feat/rspack-2-support -- <paths>`; stack each
84+
branch on the previous one (PR 5's branch on PR 2's if reviewed in
85+
parallel, rebased as the stack merges).
86+
- Re-run per-branch: `pnpm typecheck && pnpm build && pnpm test` in
87+
`packages/repack`, `npx biome check`, plus the doc 08 smoke tests for
88+
PRs 3–5.
89+
- Split the single changeset (`.changeset/rspack-2-support.md`) across the
90+
stack as described above.
91+
- After all stack PRs are open: close #1393 with a comment linking the stack
92+
and this folder.
93+
94+
## Context for a fresh start (read this first)
95+
96+
Reading order for picking this effort up without prior context:
97+
98+
1. [README.md](./README.md) — index + TL;DR
99+
2. [09 (this doc)](./09-pr-split-plan.md) — current state + next actions
100+
3. [08-implementation-notes.md](./08-implementation-notes.md) — what's on the
101+
reference branch and the technical landmines
102+
4. Docs 01–07 as needed for the *why* behind any specific change
103+
104+
Key working agreements captured along the way:
105+
- **No type casts** (`as X` / `as unknown as X`) — use `satisfies`, narrowing,
106+
or fix the root cause; irreducible cases get a full reasoning comment and a
107+
review call-out (details in doc 08).
108+
- Decisions get recorded inline in these docs with dates (see docs 04/06).
109+
- `agent_context/` lifecycle: living while in flight, settled once merged —
110+
follow-ups get a new dated folder.

agent_context/rspackv2-jul2026/README.md

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
# Rspack 2.0 Support — Research & Planning
22

3-
> Status: **Implemented on this branch** — core dual-version support (plan phases 0–2)
4-
> is in `packages/repack`; all decisions recorded (Q1–Q5 in doc 04, React Refresh in
5-
> doc 06), V1–V11 verification executed with no blockers (doc 07).
6-
> Remaining follow-ups: CI matrix + tester-app/metro-compat validation under v2 (phase 3)
7-
> and flipping init/templates/website defaults to v2 (phase 4).
8-
> Branch: `feat/rspack-2-support`
3+
> Status: **Implemented on the reference branch, pending PR split** — core
4+
> dual-version support (plan phases 0–2) is complete and verified on
5+
> `feat/rspack-2-support` (kept as a reference; draft PR #1393 to be closed in
6+
> favor of a PR stack). **Where we left off + next actions: see
7+
> [09-pr-split-plan.md](./09-pr-split-plan.md).**
8+
> All decisions recorded (Q1–Q5 in doc 04, React Refresh in doc 06), V1–V11
9+
> verification executed with no blockers (doc 07), implementation details in
10+
> doc 08.
911
> Last updated: 2026-07-02
1012
1113
This folder tracks the investigation and plan for adding Rspack 2.0 support to Re.Pack
@@ -22,6 +24,8 @@ while keeping Rspack 1.x working (dual-version support).
2224
| [05-user-benefits.md](./05-user-benefits.md) | What Rspack 2.0 gives Re.Pack users (performance, bundle size, DX) |
2325
| [06-react-refresh-deep-dive.md](./06-react-refresh-deep-dive.md) | Deep dive: what `deprecated_runtimePaths` is, why v2 removed it, and the supported v2 approach (`injectEntry`/`reactRefreshLoader` options) |
2426
| [07-verification-results.md](./07-verification-results.md) | Executed V1–V11 verification results against `@rspack/[email protected]` — no blockers; two impact-analysis revisions and one new work item (perfetto tracing) |
27+
| [08-implementation-notes.md](./08-implementation-notes.md) | What was built on the reference branch: commits, technical landmines (Jest ESM escape, v2 type fallout patterns), working agreements, verification performed |
28+
| [09-pr-split-plan.md](./09-pr-split-plan.md) | **Where we left off** — the agreed PR stack (docs → foundations → types → config routing → React Refresh), branch/PR strategy, maintainer feedback, and reading order for a fresh start |
2529

2630
## TL;DR
2731

0 commit comments

Comments
 (0)