Skip to content

feat: compile the application with the React Compiler - #2015

Draft
OliverDudgeon wants to merge 1 commit into
feat/2007-react-compiler-compatfrom
t3code/enable-react-compiler-eslint
Draft

feat: compile the application with the React Compiler#2015
OliverDudgeon wants to merge 1 commit into
feat/2007-react-compiler-compatfrom
t3code/enable-react-compiler-eslint

Conversation

@OliverDudgeon

@OliverDudgeon OliverDudgeon commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

Stacked on #2026merge that first. This PR is only the switch; the compatibility work that makes the switch safe lives there.

Turns the React Compiler on as its native Rust port inside Turbopack, so there is no Babel plugin and no new dependency.

Held back deliberately

experimental.turbopackRustReactCompiler is marked experimental and "not recommended for production". This PR stays parked until that settles. #2026 carries all the actual code changes and can land without waiting.

What it does

next.config.mjs sets reactCompiler: true and experimental.turbopackRustReactCompiler: true, introduced in Next 16.3. Per the option's docs, this runs the compiler as native code inside Turbopack, so babel-plugin-react-compiler is not needed.

The same docs state the option "is only supported with Turbopack. Using it with webpack will throw an error." #2025 already moved dev/build to Turbopack, so the only remaining webpack pins are the three analyze scripts, which give theirs up here.

Evidence it actually runs

  • Both next build and next dev print ✓ turbopackRustReactCompiler.
  • Compiler artifacts are in the output: useMemoCache appears 23 times across client chunks with the compiler on and 5 with it off (those 5 being React's own runtime export).
  • next dev is ready in ~215ms.

Verification

pnpm lint (--max-warnings=0), pnpm tsc and pnpm build all pass. pnpm test is fully green: 238 acceptance, 808 node and component.

🤖 Generated with Claude Code

@OliverDudgeon
OliverDudgeon marked this pull request as draft August 27, 2026 13:32
@OliverDudgeon

Copy link
Copy Markdown
Collaborator Author

This currently uses the babel plugin. We should use the new rust one via SWC.

@OliverDudgeon

Copy link
Copy Markdown
Collaborator Author

Pushed, still draft — this cannot land as it stands. The resolveAlias stub for paper.js/jsdom is a workaround, and #2020 now tracks removing it and finishing the Turbopack migration properly. next.config.mjs points at that issue from the workaround itself.

Two changes since the last push:

One finding that bears directly on #2020, recorded there in full: updating ketcher will not fix this. ketcher-core declares paper@^0.12.18 at 3.12.0 (ours), 3.17.2 (latest stable), 3.18.0-rc.4 and 3.19.0-rc.2 alike, and paper has not published since July 2024. The real cause is that Turbopack does not honour paper's own browser field, which already maps the specifier to false — webpack does, which is why this only appears after the bundler switch.

Still green on this branch: pnpm lint, pnpm tsc, pnpm build, 238 acceptance and 808 node/component tests.

@OliverDudgeon
OliverDudgeon force-pushed the t3code/enable-react-compiler-eslint branch from 66ee588 to 9fe10ae Compare August 27, 2026 20:20
OliverDudgeon added a commit that referenced this pull request Aug 27, 2026
paper's published manifest declares no dependencies, optionalDependencies or
peerDependencies at all — jsdom and canvas are devDependencies, which
consumers never install. `dist/node/canvas.js` requires both anyway. The
canvas require sits in a try/catch; the jsdom one does not, so bundlers
resolve `jsdom/lib/jsdom/living/generated/utils` statically and nothing in
this tree provides it.

pnpm is behaving correctly. Flat hoisting would let any unrelated jsdom in
the tree silently satisfy the unqualified require; pnpm's isolated
node_modules cannot, so the upstream bug is unmaskable.

The bug is latent under the current webpack build, because the sketcher never
reaches the server compilation. It becomes fatal under Turbopack, which keeps
it in the SSR graph — every failing import trace is an SSR one, never the
browser graph. Landing the patch first means the Turbopack migration (#2015)
never has to introduce the `turbopack.resolveAlias` workaround it currently
carries.

Patch the require with a `webpackIgnore: true` magic comment, which both
webpack and Turbopack honour on `require()` expressions, so the specifier is
left for Node to resolve at runtime instead of being bundled. Runtime
behaviour is unchanged: the line is only reached once `require('canvas')` has
succeeded, and installing canvas is what brings jsdom into the tree.

Scoping is what makes this better than the alias. `resolveAlias` redirects
the specifier for every importer in the graph, so a genuine jsdom consumer
would silently receive a no-op stub. The patch touches only paper.

Also delete `src/stubs/jsdom-utils.stub.js`, which the alias used to point
at. It has been dead code here since 84a9309 removed the alias that
referenced it, and the patch means nothing needs it again.

Considered and rejected: declaring jsdom as a devDependency, and repairing
paper's manifest via pnpm.packageExtensions. Both work, and both install a
large tree and pin it to <=27 — the target is a jsdom internal, removed in
28 with no semver obligation — to satisfy a require that never executes.

If a ketcher bump moves paper off 0.12.18, pnpm fails the install with
ERR_PNPM_UNUSED_PATCH rather than silently reverting to a broken build.

Worth fixing upstream regardless: paper should declare jsdom as an
optionalDependency next to canvas, or not require it statically.

Closes #2020

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
OliverDudgeon added a commit that referenced this pull request Aug 27, 2026
paper's published manifest declares no dependencies, optionalDependencies or
peerDependencies at all — jsdom and canvas are devDependencies, which
consumers never install. `dist/node/canvas.js` requires both anyway. The
canvas require sits in a try/catch; the jsdom one does not, so bundlers
resolve `jsdom/lib/jsdom/living/generated/utils` statically and nothing in
this tree provides it.

pnpm is behaving correctly. Flat hoisting would let any unrelated jsdom in
the tree silently satisfy the unqualified require; pnpm's isolated
node_modules cannot, so the upstream bug is unmaskable.

The bug is latent under the current webpack build, because the sketcher never
reaches the server compilation. It becomes fatal under Turbopack, which keeps
it in the SSR graph — every failing import trace is an SSR one, never the
browser graph. Landing the patch first means the Turbopack migration (#2015)
never has to introduce the `turbopack.resolveAlias` workaround it currently
carries.

Patch the require with a `webpackIgnore: true` magic comment, which both
webpack and Turbopack honour on `require()` expressions, so the specifier is
left for Node to resolve at runtime instead of being bundled. Runtime
behaviour is unchanged: the line is only reached once `require('canvas')` has
succeeded, and installing canvas is what brings jsdom into the tree.

Scoping is what makes this better than the alias. `resolveAlias` redirects
the specifier for every importer in the graph, so a genuine jsdom consumer
would silently receive a no-op stub. The patch touches only paper.

Also delete `src/stubs/jsdom-utils.stub.js`, which the alias used to point
at. It has been dead code here since 84a9309 removed the alias that
referenced it, and the patch means nothing needs it again.

Considered and rejected: declaring jsdom as a devDependency, and repairing
paper's manifest via pnpm.packageExtensions. Both work, and both install a
large tree and pin it to <=27 — the target is a jsdom internal, removed in
28 with no semver obligation — to satisfy a require that never executes.

If a ketcher bump moves paper off 0.12.18, pnpm fails the install with
ERR_PNPM_UNUSED_PATCH rather than silently reverting to a broken build.

Worth fixing upstream regardless: paper should declare jsdom as an
optionalDependency next to canvas, or not require it statically.

Closes #2020

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
im-semantic-release Bot pushed a commit that referenced this pull request Aug 27, 2026
# [7.0.0-dev.18](7.0.0-dev.17...7.0.0-dev.18) (2026-08-27)

### Bug Fixes

* patch paper so bundlers stop resolving its undeclared jsdom require ([97c3d95](97c3d95)), closes [#2015](#2015) [#2020](#2020)
@OliverDudgeon
OliverDudgeon force-pushed the t3code/enable-react-compiler-eslint branch 2 times, most recently from c2fe14c to 2120e75 Compare August 28, 2026 09:53
@OliverDudgeon
OliverDudgeon changed the base branch from dev to feat/2007-react-compiler-compat August 28, 2026 09:53
@OliverDudgeon
OliverDudgeon force-pushed the t3code/enable-react-compiler-eslint branch from 2120e75 to b29d8d1 Compare August 28, 2026 10:06
@OliverDudgeon
OliverDudgeon force-pushed the feat/2007-react-compiler-compat branch from dbefda4 to 3b4218a Compare August 28, 2026 10:15
Turns the React Compiler on as its native Rust port inside Turbopack, so there
is no Babel plugin and no new dependency.

`next.config.mjs` sets `reactCompiler: true` and
`experimental.turbopackRustReactCompiler: true`, introduced in Next 16.3. Per
the option's docs this runs the compiler as native code inside Turbopack, which
`dev` already builds and serves with, so `babel-plugin-react-compiler` is not
needed.

The same docs state the option is only supported with Turbopack and throws
under webpack, so the three `analyze` scripts give up their remaining
`--webpack` pin.

Builds on the compatibility work that turned on every `eslint-plugin-react-hooks`
v7 diagnostic and cleared the tree, so no component is silently skipped.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@OliverDudgeon
OliverDudgeon force-pushed the t3code/enable-react-compiler-eslint branch from b29d8d1 to bba9b46 Compare August 28, 2026 10:15
@OliverDudgeon

Copy link
Copy Markdown
Collaborator Author

This is essentially working. However, the nextjs side is still marked as experimental. This can be promoted out of draft when that changes.

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