From 5568bd55efb0caa01b1d09e2612ac3a0885f1647 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 1 Oct 2025 19:31:12 +0900 Subject: [PATCH 01/16] feat: expose virtual for simpler preamble setup on ssr --- packages/common/package.json | 3 ++ packages/common/refresh-utils.ts | 36 +++++++++++++++++++++++ packages/plugin-react/package.json | 5 +++- packages/plugin-react/preamble.d.ts | 1 + packages/plugin-react/src/index.ts | 5 ++++ playground/ssr-react/src/entry-client.jsx | 1 + playground/ssr-react/vite.config.js | 10 +++++-- pnpm-lock.yaml | 6 +++- 8 files changed, 62 insertions(+), 5 deletions(-) create mode 100644 packages/plugin-react/preamble.d.ts diff --git a/packages/common/package.json b/packages/common/package.json index 1bb806f8c..97c1eb7c2 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -7,6 +7,9 @@ ".": "./index.ts", "./refresh-runtime": "./refresh-runtime.js" }, + "dependencies": { + "@rolldown/pluginutils": "1.0.0-beta.41" + }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0" } diff --git a/packages/common/refresh-utils.ts b/packages/common/refresh-utils.ts index db0e587e4..ef9dc00f6 100644 --- a/packages/common/refresh-utils.ts +++ b/packages/common/refresh-utils.ts @@ -1,3 +1,6 @@ +import type { Plugin } from 'vite' +import { exactRegex } from '@rolldown/pluginutils' + export const runtimePublicPath = '/@react-refresh' const reactCompRE = /extends\s+(?:React\.)?(?:Pure)?Component/ @@ -60,3 +63,36 @@ function $RefreshSig$() { return RefreshRuntime.createSignatureFunctionForTransf return newCode } + +export function virtualPreamblePlugin({ + name, + isEnabled, +}: { + name: string + isEnabled: () => boolean +}): Plugin { + return { + name: 'vite:react-virtual-preamble', + resolveId: { + order: 'pre', + filter: { id: exactRegex(name) }, + handler(source) { + if (source === name) { + return '\0' + source + } + }, + }, + load: { + filter: { id: exactRegex('\0' + name) }, + handler(id) { + if (id === '\0' + name) { + if (isEnabled()) { + // vite dev import analysis can rewrite base + return preambleCode.replace('__BASE__', '/') + } + return '' + } + }, + }, + } +} diff --git a/packages/plugin-react/package.json b/packages/plugin-react/package.json index 8c7370bf6..b630a36c8 100644 --- a/packages/plugin-react/package.json +++ b/packages/plugin-react/package.json @@ -20,7 +20,10 @@ "dist" ], "type": "module", - "exports": "./dist/index.js", + "exports": { + ".": "./dist/index.js", + "./preamble": "./preamble.d.ts" + }, "scripts": { "dev": "tsdown --watch ./src --watch ../common", "build": "tsdown", diff --git a/packages/plugin-react/preamble.d.ts b/packages/plugin-react/preamble.d.ts new file mode 100644 index 000000000..336ce12bb --- /dev/null +++ b/packages/plugin-react/preamble.d.ts @@ -0,0 +1 @@ +export {} diff --git a/packages/plugin-react/src/index.ts b/packages/plugin-react/src/index.ts index a01f693e5..41993f281 100644 --- a/packages/plugin-react/src/index.ts +++ b/packages/plugin-react/src/index.ts @@ -12,6 +12,7 @@ import { preambleCode, runtimePublicPath, silenceUseClientWarning, + virtualPreamblePlugin, } from '@vitejs/react-common' import { exactRegex, @@ -524,6 +525,10 @@ export default function viteReact(opts: Options = {}): Plugin[] { ? [viteRefreshWrapper, viteConfigPost, viteReactRefreshFullBundleMode] : []), viteReactRefresh, + virtualPreamblePlugin({ + name: '@vitejs/plugin-react/preamble', + isEnabled: () => !skipFastRefresh && !isFullBundle, + }), ] } diff --git a/playground/ssr-react/src/entry-client.jsx b/playground/ssr-react/src/entry-client.jsx index bb2769717..62b8418e0 100644 --- a/playground/ssr-react/src/entry-client.jsx +++ b/playground/ssr-react/src/entry-client.jsx @@ -1,3 +1,4 @@ +import '@vitejs/plugin-react/preamble' import ReactDOM from 'react-dom/client' import { App } from './App' diff --git a/playground/ssr-react/vite.config.js b/playground/ssr-react/vite.config.js index f9258a1d0..5922ac765 100644 --- a/playground/ssr-react/vite.config.js +++ b/playground/ssr-react/vite.config.js @@ -41,10 +41,14 @@ export default defineConfig({ '/src/entry-server.jsx', ) const appHtml = render(url) - const template = await server.transformIndexHtml( - url, - fs.readFileSync(path.resolve(_dirname, 'index.html'), 'utf-8'), + const template = fs.readFileSync( + path.resolve(_dirname, 'index.html'), + 'utf-8', ) + // const template = await server.transformIndexHtml( + // url, + // fs.readFileSync(path.resolve(_dirname, 'index.html'), 'utf-8'), + // ) const html = template.replace(``, appHtml) res.setHeader('content-type', 'text/html').end(html) } catch (e) { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5f4e6ae46..d65a67fe6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -78,7 +78,11 @@ importers: specifier: ^3.2.4 version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.7)(jiti@2.5.1)(lightningcss@1.30.1)(yaml@2.8.1) - packages/common: {} + packages/common: + dependencies: + '@rolldown/pluginutils': + specifier: 1.0.0-beta.41 + version: 1.0.0-beta.41 packages/plugin-react: dependencies: From c6c29b48c1907300001e64c8d68187e6d3f4a175 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 1 Oct 2025 21:11:09 +0900 Subject: [PATCH 02/16] feat: auto inject hmr preamble without `transformIndexHtml` --- packages/common/refresh-utils.ts | 11 ++++++++++- packages/plugin-react/src/index.ts | 10 +++++----- playground/ssr-react/src/entry-client.jsx | 1 - 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/packages/common/refresh-utils.ts b/packages/common/refresh-utils.ts index ef9dc00f6..5b37df2aa 100644 --- a/packages/common/refresh-utils.ts +++ b/packages/common/refresh-utils.ts @@ -12,7 +12,8 @@ export const preambleCode = `import { injectIntoGlobalHook } from "__BASE__${run )}"; injectIntoGlobalHook(window); window.$RefreshReg$ = () => {}; -window.$RefreshSig$ = () => (type) => type;` +window.$RefreshSig$ = () => (type) => type; +` export const getPreambleCode = (base: string): string => preambleCode.replace('__BASE__', base) @@ -94,5 +95,13 @@ export function virtualPreamblePlugin({ } }, }, + transform: { + filter: { code: /__REACT_DEVTOOLS_GLOBAL_HOOK__/ }, + handler(code) { + if (isEnabled() && code.includes('__REACT_DEVTOOLS_GLOBAL_HOOK__')) { + return `import ${JSON.stringify(name)};` + code + } + }, + }, } } diff --git a/packages/plugin-react/src/index.ts b/packages/plugin-react/src/index.ts index 41993f281..1a798cd37 100644 --- a/packages/plugin-react/src/index.ts +++ b/packages/plugin-react/src/index.ts @@ -510,11 +510,11 @@ export default function viteReact(opts: Options = {}): Plugin[] { transformIndexHtml() { if (!skipFastRefresh && !isFullBundle) return [ - { - tag: 'script', - attrs: { type: 'module' }, - children: getPreambleCode(base), - }, + // { + // tag: 'script', + // attrs: { type: 'module' }, + // children: getPreambleCode(base), + // }, ] }, } diff --git a/playground/ssr-react/src/entry-client.jsx b/playground/ssr-react/src/entry-client.jsx index 62b8418e0..bb2769717 100644 --- a/playground/ssr-react/src/entry-client.jsx +++ b/playground/ssr-react/src/entry-client.jsx @@ -1,4 +1,3 @@ -import '@vitejs/plugin-react/preamble' import ReactDOM from 'react-dom/client' import { App } from './App' From a6edb507828d45c3c713b96ffae68c3e4c2ffca6 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 1 Oct 2025 21:17:37 +0900 Subject: [PATCH 03/16] chore: comment --- packages/common/refresh-utils.ts | 1 + packages/plugin-react/package.json | 3 +-- packages/plugin-react/preamble.d.ts | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) delete mode 100644 packages/plugin-react/preamble.d.ts diff --git a/packages/common/refresh-utils.ts b/packages/common/refresh-utils.ts index 5b37df2aa..b5c642fc8 100644 --- a/packages/common/refresh-utils.ts +++ b/packages/common/refresh-utils.ts @@ -98,6 +98,7 @@ export function virtualPreamblePlugin({ transform: { filter: { code: /__REACT_DEVTOOLS_GLOBAL_HOOK__/ }, handler(code) { + // this is expected to be either `react` or `react-dom/client` if (isEnabled() && code.includes('__REACT_DEVTOOLS_GLOBAL_HOOK__')) { return `import ${JSON.stringify(name)};` + code } diff --git a/packages/plugin-react/package.json b/packages/plugin-react/package.json index b630a36c8..54d2ee0ec 100644 --- a/packages/plugin-react/package.json +++ b/packages/plugin-react/package.json @@ -21,8 +21,7 @@ ], "type": "module", "exports": { - ".": "./dist/index.js", - "./preamble": "./preamble.d.ts" + ".": "./dist/index.js" }, "scripts": { "dev": "tsdown --watch ./src --watch ../common", diff --git a/packages/plugin-react/preamble.d.ts b/packages/plugin-react/preamble.d.ts deleted file mode 100644 index 336ce12bb..000000000 --- a/packages/plugin-react/preamble.d.ts +++ /dev/null @@ -1 +0,0 @@ -export {} From f579b378355c52da268a64456208dea51285bafc Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 1 Oct 2025 21:18:25 +0900 Subject: [PATCH 04/16] chore: ensure once --- packages/common/refresh-utils.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/common/refresh-utils.ts b/packages/common/refresh-utils.ts index b5c642fc8..b4db243e6 100644 --- a/packages/common/refresh-utils.ts +++ b/packages/common/refresh-utils.ts @@ -10,9 +10,12 @@ const refreshContentRE = /\$RefreshReg\$\(/ export const preambleCode = `import { injectIntoGlobalHook } from "__BASE__${runtimePublicPath.slice( 1, )}"; +if (!window.__vite_plugin_react_preamble_installed__) { injectIntoGlobalHook(window); window.$RefreshReg$ = () => {}; window.$RefreshSig$ = () => (type) => type; +} +window.__vite_plugin_react_preamble_installed__ = true; ` export const getPreambleCode = (base: string): string => From 1e2327fb078cad9d71b2d47d43c2a45bc2a47f02 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 1 Oct 2025 21:19:51 +0900 Subject: [PATCH 05/16] fix: client only --- packages/common/refresh-utils.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/common/refresh-utils.ts b/packages/common/refresh-utils.ts index b4db243e6..a5b0c0e06 100644 --- a/packages/common/refresh-utils.ts +++ b/packages/common/refresh-utils.ts @@ -100,7 +100,8 @@ export function virtualPreamblePlugin({ }, transform: { filter: { code: /__REACT_DEVTOOLS_GLOBAL_HOOK__/ }, - handler(code) { + handler(code, _id, options) { + if (options?.ssr) return // this is expected to be either `react` or `react-dom/client` if (isEnabled() && code.includes('__REACT_DEVTOOLS_GLOBAL_HOOK__')) { return `import ${JSON.stringify(name)};` + code From 9644f5e23bcd9e5e11eed364f725735565a35d7f Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 1 Oct 2025 21:23:41 +0900 Subject: [PATCH 06/16] cleanup --- packages/common/refresh-utils.ts | 15 ++++++++------- packages/plugin-react/src/index.ts | 1 - 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/common/refresh-utils.ts b/packages/common/refresh-utils.ts index a5b0c0e06..9c766d6a9 100644 --- a/packages/common/refresh-utils.ts +++ b/packages/common/refresh-utils.ts @@ -69,27 +69,27 @@ function $RefreshSig$() { return RefreshRuntime.createSignatureFunctionForTransf } export function virtualPreamblePlugin({ - name, isEnabled, }: { - name: string isEnabled: () => boolean }): Plugin { + const VIRTUAL_NAME = 'virtual:@vitejs/plugin-react/preamble' return { name: 'vite:react-virtual-preamble', + apply: 'serve', resolveId: { order: 'pre', - filter: { id: exactRegex(name) }, + filter: { id: exactRegex(VIRTUAL_NAME) }, handler(source) { - if (source === name) { + if (source === VIRTUAL_NAME) { return '\0' + source } }, }, load: { - filter: { id: exactRegex('\0' + name) }, + filter: { id: exactRegex('\0' + VIRTUAL_NAME) }, handler(id) { - if (id === '\0' + name) { + if (id === '\0' + VIRTUAL_NAME) { if (isEnabled()) { // vite dev import analysis can rewrite base return preambleCode.replace('__BASE__', '/') @@ -102,9 +102,10 @@ export function virtualPreamblePlugin({ filter: { code: /__REACT_DEVTOOLS_GLOBAL_HOOK__/ }, handler(code, _id, options) { if (options?.ssr) return + // this is expected to be either `react` or `react-dom/client` if (isEnabled() && code.includes('__REACT_DEVTOOLS_GLOBAL_HOOK__')) { - return `import ${JSON.stringify(name)};` + code + return `import ${JSON.stringify(VIRTUAL_NAME)};` + code } }, }, diff --git a/packages/plugin-react/src/index.ts b/packages/plugin-react/src/index.ts index 1a798cd37..4fcfee2f7 100644 --- a/packages/plugin-react/src/index.ts +++ b/packages/plugin-react/src/index.ts @@ -526,7 +526,6 @@ export default function viteReact(opts: Options = {}): Plugin[] { : []), viteReactRefresh, virtualPreamblePlugin({ - name: '@vitejs/plugin-react/preamble', isEnabled: () => !skipFastRefresh && !isFullBundle, }), ] From 64d14ce861f29d1ee5ea70db734a42b2f1c92f63 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 1 Oct 2025 21:25:04 +0900 Subject: [PATCH 07/16] chore: comment --- packages/common/refresh-utils.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/common/refresh-utils.ts b/packages/common/refresh-utils.ts index 9c766d6a9..89fdeb443 100644 --- a/packages/common/refresh-utils.ts +++ b/packages/common/refresh-utils.ts @@ -103,7 +103,8 @@ export function virtualPreamblePlugin({ handler(code, _id, options) { if (options?.ssr) return - // this is expected to be either `react` or `react-dom/client` + // this is expected to be either `react` or `react-dom/client`. + // both are optimized to be esm during dev. if (isEnabled() && code.includes('__REACT_DEVTOOLS_GLOBAL_HOOK__')) { return `import ${JSON.stringify(VIRTUAL_NAME)};` + code } From bab8a7b3c29d3039824d32ac4a6c176d289d7c2d Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 1 Oct 2025 21:29:00 +0900 Subject: [PATCH 08/16] chore: cleanup --- packages/common/refresh-utils.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/common/refresh-utils.ts b/packages/common/refresh-utils.ts index 89fdeb443..98d242328 100644 --- a/packages/common/refresh-utils.ts +++ b/packages/common/refresh-utils.ts @@ -10,13 +10,9 @@ const refreshContentRE = /\$RefreshReg\$\(/ export const preambleCode = `import { injectIntoGlobalHook } from "__BASE__${runtimePublicPath.slice( 1, )}"; -if (!window.__vite_plugin_react_preamble_installed__) { injectIntoGlobalHook(window); window.$RefreshReg$ = () => {}; -window.$RefreshSig$ = () => (type) => type; -} -window.__vite_plugin_react_preamble_installed__ = true; -` +window.$RefreshSig$ = () => (type) => type;` export const getPreambleCode = (base: string): string => preambleCode.replace('__BASE__', base) From 1e58be79d13ed3ba084106adcd412a979b5a93a1 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 1 Oct 2025 21:30:26 +0900 Subject: [PATCH 09/16] chore: cleanup --- packages/plugin-react/package.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/plugin-react/package.json b/packages/plugin-react/package.json index 54d2ee0ec..8c7370bf6 100644 --- a/packages/plugin-react/package.json +++ b/packages/plugin-react/package.json @@ -20,9 +20,7 @@ "dist" ], "type": "module", - "exports": { - ".": "./dist/index.js" - }, + "exports": "./dist/index.js", "scripts": { "dev": "tsdown --watch ./src --watch ../common", "build": "tsdown", From 5d415cea31c3b0f115306eb24c251c3f6b077239 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 1 Oct 2025 21:42:31 +0900 Subject: [PATCH 10/16] chore: comment --- packages/common/refresh-utils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/common/refresh-utils.ts b/packages/common/refresh-utils.ts index 98d242328..6586cf49a 100644 --- a/packages/common/refresh-utils.ts +++ b/packages/common/refresh-utils.ts @@ -99,8 +99,8 @@ export function virtualPreamblePlugin({ handler(code, _id, options) { if (options?.ssr) return - // this is expected to be either `react` or `react-dom/client`. - // both are optimized to be esm during dev. + // this is expected to match `react`, `react-dom`, and `react-dom/client`. + // they are all optimized to be esm during dev. if (isEnabled() && code.includes('__REACT_DEVTOOLS_GLOBAL_HOOK__')) { return `import ${JSON.stringify(VIRTUAL_NAME)};` + code } From e2861c92a27e63aa6d70e74dc10a22140f2ce4c5 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 1 Oct 2025 21:43:29 +0900 Subject: [PATCH 11/16] fix: skip self --- packages/common/refresh-utils.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/common/refresh-utils.ts b/packages/common/refresh-utils.ts index 6586cf49a..b812ef6e2 100644 --- a/packages/common/refresh-utils.ts +++ b/packages/common/refresh-utils.ts @@ -96,8 +96,9 @@ export function virtualPreamblePlugin({ }, transform: { filter: { code: /__REACT_DEVTOOLS_GLOBAL_HOOK__/ }, - handler(code, _id, options) { + handler(code, id, options) { if (options?.ssr) return + if (id === runtimePublicPath) return // this is expected to match `react`, `react-dom`, and `react-dom/client`. // they are all optimized to be esm during dev. From 594f42b0980639ada9b83a021ed0f5661b55e8f5 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 1 Oct 2025 22:06:39 +0900 Subject: [PATCH 12/16] fix: handle reactRefreshHost --- packages/common/refresh-utils.ts | 18 ++++++++++++------ packages/plugin-react/src/index.ts | 1 + playground/react/vite.config.ts | 6 +++++- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/packages/common/refresh-utils.ts b/packages/common/refresh-utils.ts index b812ef6e2..da9c9233c 100644 --- a/packages/common/refresh-utils.ts +++ b/packages/common/refresh-utils.ts @@ -64,12 +64,15 @@ function $RefreshSig$() { return RefreshRuntime.createSignatureFunctionForTransf return newCode } -export function virtualPreamblePlugin({ - isEnabled, -}: { +export function virtualPreamblePlugin(opts: { isEnabled: () => boolean + reactRefreshHost?: string }): Plugin { const VIRTUAL_NAME = 'virtual:@vitejs/plugin-react/preamble' + let importSource = VIRTUAL_NAME + if (opts.reactRefreshHost) { + importSource = opts.reactRefreshHost + '/@id/__x00__' + VIRTUAL_NAME + } return { name: 'vite:react-virtual-preamble', apply: 'serve', @@ -86,7 +89,7 @@ export function virtualPreamblePlugin({ filter: { id: exactRegex('\0' + VIRTUAL_NAME) }, handler(id) { if (id === '\0' + VIRTUAL_NAME) { - if (isEnabled()) { + if (opts.isEnabled()) { // vite dev import analysis can rewrite base return preambleCode.replace('__BASE__', '/') } @@ -102,8 +105,11 @@ export function virtualPreamblePlugin({ // this is expected to match `react`, `react-dom`, and `react-dom/client`. // they are all optimized to be esm during dev. - if (isEnabled() && code.includes('__REACT_DEVTOOLS_GLOBAL_HOOK__')) { - return `import ${JSON.stringify(VIRTUAL_NAME)};` + code + if ( + opts.isEnabled() && + code.includes('__REACT_DEVTOOLS_GLOBAL_HOOK__') + ) { + return `import ${JSON.stringify(importSource)};` + code } }, }, diff --git a/packages/plugin-react/src/index.ts b/packages/plugin-react/src/index.ts index 4fcfee2f7..dbdbd9ca0 100644 --- a/packages/plugin-react/src/index.ts +++ b/packages/plugin-react/src/index.ts @@ -527,6 +527,7 @@ export default function viteReact(opts: Options = {}): Plugin[] { viteReactRefresh, virtualPreamblePlugin({ isEnabled: () => !skipFastRefresh && !isFullBundle, + reactRefreshHost: opts.reactRefreshHost, }), ] } diff --git a/playground/react/vite.config.ts b/playground/react/vite.config.ts index d257635de..192210e3a 100644 --- a/playground/react/vite.config.ts +++ b/playground/react/vite.config.ts @@ -4,7 +4,11 @@ import type { UserConfig } from 'vite' const config: UserConfig = { server: { port: 8902 /* Should be unique */ }, mode: 'development', - plugins: [react()], + plugins: [ + react({ + reactRefreshHost: 'http://localhost:8902', + }), + ], build: { // to make tests faster minify: false, From ce2f41d456946672c5974d418dae3dde68b4fb42 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 1 Oct 2025 22:18:36 +0900 Subject: [PATCH 13/16] chore: cleanup --- packages/plugin-react/src/index.ts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/packages/plugin-react/src/index.ts b/packages/plugin-react/src/index.ts index dbdbd9ca0..c4ba1c374 100644 --- a/packages/plugin-react/src/index.ts +++ b/packages/plugin-react/src/index.ts @@ -507,16 +507,6 @@ export default function viteReact(opts: Options = {}): Plugin[] { } }, }, - transformIndexHtml() { - if (!skipFastRefresh && !isFullBundle) - return [ - // { - // tag: 'script', - // attrs: { type: 'module' }, - // children: getPreambleCode(base), - // }, - ] - }, } return [ From fecb9a74276188658eb77f62711e996ffc6a591a Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 1 Oct 2025 22:39:19 +0900 Subject: [PATCH 14/16] chore: cleanup --- playground/ssr-react/vite.config.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/playground/ssr-react/vite.config.js b/playground/ssr-react/vite.config.js index 5922ac765..6a658a0fe 100644 --- a/playground/ssr-react/vite.config.js +++ b/playground/ssr-react/vite.config.js @@ -45,10 +45,6 @@ export default defineConfig({ path.resolve(_dirname, 'index.html'), 'utf-8', ) - // const template = await server.transformIndexHtml( - // url, - // fs.readFileSync(path.resolve(_dirname, 'index.html'), 'utf-8'), - // ) const html = template.replace(``, appHtml) res.setHeader('content-type', 'text/html').end(html) } catch (e) { From 52aa8be889d5d50904557f4f4b01852ba2e71f5c Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 1 Oct 2025 22:44:15 +0900 Subject: [PATCH 15/16] update swc and oxc plugins --- packages/plugin-react-oxc/src/index.ts | 22 ++++++++++------------ packages/plugin-react-swc/src/index.ts | 17 +++++------------ 2 files changed, 15 insertions(+), 24 deletions(-) diff --git a/packages/plugin-react-oxc/src/index.ts b/packages/plugin-react-oxc/src/index.ts index e42b7356a..0702c0ddc 100644 --- a/packages/plugin-react-oxc/src/index.ts +++ b/packages/plugin-react-oxc/src/index.ts @@ -4,9 +4,9 @@ import { readFileSync } from 'node:fs' import type { BuildOptions, Plugin } from 'vite' import { addRefreshWrapper, - getPreambleCode, runtimePublicPath, silenceUseClientWarning, + virtualPreamblePlugin, } from '@vitejs/react-common' import { exactRegex } from '@rolldown/pluginutils' @@ -143,17 +143,15 @@ export default function viteReact(opts: Options = {}): Plugin[] { return newCode ? { code: newCode, map: null } : undefined }, }, - transformIndexHtml(_, config) { - if (!skipFastRefresh) - return [ - { - tag: 'script', - attrs: { type: 'module' }, - children: getPreambleCode(config.server!.config.base), - }, - ] - }, } - return [viteConfig, viteConfigPost, viteRefreshRuntime, viteRefreshWrapper] + return [ + viteConfig, + viteConfigPost, + viteRefreshRuntime, + viteRefreshWrapper, + virtualPreamblePlugin({ + isEnabled: () => !skipFastRefresh, + }) as any, // rolldown-vite type mismatch + ] } diff --git a/packages/plugin-react-swc/src/index.ts b/packages/plugin-react-swc/src/index.ts index 425bac15c..c65108cc9 100644 --- a/packages/plugin-react-swc/src/index.ts +++ b/packages/plugin-react-swc/src/index.ts @@ -13,9 +13,9 @@ import { import type { Plugin } from 'vite' import { addRefreshWrapper, - getPreambleCode, runtimePublicPath, silenceUseClientWarning, + virtualPreamblePlugin, } from '@vitejs/react-common' import * as vite from 'vite' import { exactRegex } from '@rolldown/pluginutils' @@ -165,17 +165,6 @@ const react = (_options?: Options): Plugin[] => { ) } }, - transformIndexHtml: (_, config) => { - if (!hmrDisabled) { - return [ - { - tag: 'script', - attrs: { type: 'module' }, - children: getPreambleCode(config.server!.config.base), - }, - ] - } - }, async transform(code, _id, transformOptions) { const id = _id.split('?')[0] const refresh = !transformOptions?.ssr && !hmrDisabled @@ -205,6 +194,10 @@ const react = (_options?: Options): Plugin[] => { return { code: newCode ?? result.code, map: result.map } }, }, + virtualPreamblePlugin({ + isEnabled: () => !hmrDisabled, + reactRefreshHost: options.reactRefreshHost, + }), options.plugins ? { name: 'vite:react-swc', From bcf292084282055883d59d84197d86f5055a0120 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 1 Oct 2025 22:52:47 +0900 Subject: [PATCH 16/16] fix: ensure once --- packages/common/refresh-runtime.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/common/refresh-runtime.js b/packages/common/refresh-runtime.js index e798d000e..ffee3bbab 100644 --- a/packages/common/refresh-runtime.js +++ b/packages/common/refresh-runtime.js @@ -314,6 +314,9 @@ function collectCustomHooksForSignature(type) { } export function injectIntoGlobalHook(globalObject) { + if (globalObject.__vite_plugin_react_injectIntoGlobalHook) return + globalObject.__vite_plugin_react_injectIntoGlobalHook = true + // For React Native, the global hook will be set up by require('react-devtools-core'). // That code will run before us. So we need to monkeypatch functions on existing hook.