Skip to content

fix(server): contain oxc fast refresh when React shares the dev server with Vue - #151

Merged
aoede3 merged 2 commits into
mainfrom
fix/react-refresh-vue-containment
Sep 3, 2026
Merged

aoede3 merged 2 commits into
mainfrom
fix/react-refresh-vue-containment

Conversation

@aoede3

@aoede3 aoede3 commented Sep 3, 2026

Copy link
Copy Markdown
Owner

On Rolldown Vite, @vitejs/plugin-react's config() hook turns oxc JSX fast refresh on for the whole shared development server, and @vitejs/plugin-vue transpiles a <script setup lang="ts"> block itself by spreading that resolved oxc config into its own transformWithOxc call, with no environment - so Vite's per-module refresh guards never run, oxc reads a use* call as a hook, and the Vue SSR module throws $RefreshSig$ is not defined. One serve-only, post-enforced plugin (taujs:oxc-refresh-containment) is appended to the dev chain only when React's managed compiler shares the server with Vue's renderer, and nothing broader. It overrides exactly one field, oxc.jsx.refresh, and reports that pair at boot. React edits fall back to a full reload in that composition only; React + Solid, Vue alone, React beside any other renderer, Solid + Vue and configs with no managed contribution compose exactly as before, and production was never affected (plugin-vue reads options.devServer, which is undefined at build).

A generic "any managed compiler + any non-managed renderer" gate was reviewed and rejected: it would switch React's refresh off beside an unrelated renderer and blame plugin-vue, and would report Solid as degraded when only React's refresh is affected. A temporary workaround for one named defect gates on the named keys.

Upstream: vitejs/vite-plugin-vue#798, fix pending in vitejs/vite-plugin-vue#814.

Removal trigger: when a @vitejs/plugin-vue release carries PR #814, raise packages/vue's @vitejs/plugin-vue peer floor to it and delete the plugin, its tests and the warning.

Cells: fixtures/renderer-composition/test/dev-react-vue.test.ts (real Vite dev transforms, both declaration orders, a post-start SFC, and a React-alone control that keeps $RefreshReg$); test/dev-product-path.test.ts (real createServer boot in a child process - both SSR routes 200, the warning line, level and meta); test/taujsBuild.test.ts (production pin: no refresh code in either build); packages/server/src/test/OxcRefreshContainment.test.ts (the React + Vue gate incl. React + other, Solid + Vue and React + Solid + Vue rows, config-hook arithmetic, message text).

…r with Vue

On Rolldown Vite, @vitejs/plugin-react's config() hook turns oxc JSX fast
refresh on for the whole shared development server, and @vitejs/plugin-vue
transpiles a `<script setup lang="ts">` block itself by spreading that resolved
oxc config into its own transformWithOxc call, with no environment - so Vite's
per-module refresh guards never run, oxc reads a `use*` call as a hook, and the
Vue SSR module throws `$RefreshSig$ is not defined`. The host now appends one
serve-only, post-enforced plugin (`taujs:oxc-refresh-containment`) to the dev
chain, gated on the exact pair the defect needs - React's managed compiler
sharing that server with Vue's renderer - and nothing broader: it overrides
exactly one field, `oxc.jsx.refresh`, and reports the triggering pair at boot.
React edits fall back to a full reload in that composition only; React + Solid,
Vue alone, React beside any other renderer, Solid + Vue and configs with no
managed contribution compose exactly as before, and production was never
affected (plugin-vue reads `options.devServer`, which is undefined at build).

A generic "any managed compiler + any non-managed renderer" gate was reviewed
and rejected: it would switch React's refresh off beside an unrelated renderer
and blame plugin-vue, and would report Solid as degraded when only React's
refresh is affected. A temporary workaround for one named defect gates on the
named keys.

Upstream: vitejs/vite-plugin-vue#798, fix pending in
vitejs/vite-plugin-vue#814.

Removal trigger: when a @vitejs/plugin-vue release carries PR #814, raise
packages/vue's @vitejs/plugin-vue peer floor to it and delete the plugin, its
tests and the warning.

Cells: fixtures/renderer-composition/test/dev-react-vue.test.ts (real Vite dev
transforms, both declaration orders, a post-start SFC, and a React-alone control
that keeps $RefreshReg$); test/dev-product-path.test.ts (real createServer boot
in a child process - both SSR routes 200, the warning line, level and meta);
test/taujsBuild.test.ts (production pin: no refresh code in either build);
packages/server/src/test/OxcRefreshContainment.test.ts (the React + Vue gate
incl. React + other, Solid + Vue and React + Solid + Vue rows, config-hook
arithmetic, message text).
The product-path cell pinned `[warn] <containment line>` as a plain substring of the child's
output. A shell exporting FORCE_COLOR is inherited by the child, picocolors then wraps the level
tag in ANSI codes, and the pin missed in both declaration orders. The child now boots with
FORCE_COLOR=0 and NO_COLOR=1, and captured output is ANSI-stripped before every assertion, so the
pin holds under any terminal. No product change.
@aoede3
aoede3 merged commit 83d8125 into main Sep 3, 2026
2 checks passed
@aoede3
aoede3 deleted the fix/react-refresh-vue-containment branch September 3, 2026 19:20
@github-actions github-actions Bot mentioned this pull request Sep 3, 2026
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