diff --git a/packages/common/refresh-runtime.js b/packages/common/refresh-runtime.js index 67778fc7d..e798d000e 100644 --- a/packages/common/refresh-runtime.js +++ b/packages/common/refresh-runtime.js @@ -243,7 +243,7 @@ function performReactRefresh() { } } -function register(type, id) { +export function register(type, id) { if (type === null) { return } @@ -564,10 +564,6 @@ function isPlainObject(obj) { * Plugin utils */ -export function getRefreshReg(filename) { - return (type, id) => register(type, filename + ' ' + id) -} - // Taken from https://github.com/pmmmwh/react-refresh-webpack-plugin/blob/main/lib/runtime/RefreshUtils.js#L141 // This allows to resister components not detected by SWC like styled component export function registerExportsForReactRefresh(filename, moduleExports) { diff --git a/packages/common/refresh-utils.ts b/packages/common/refresh-utils.ts index 7528bf781..db0e587e4 100644 --- a/packages/common/refresh-utils.ts +++ b/packages/common/refresh-utils.ts @@ -14,33 +14,22 @@ window.$RefreshSig$ = () => (type) => type;` export const getPreambleCode = (base: string): string => preambleCode.replace('__BASE__', base) -export const avoidSourceMapOption = Symbol() - -export function addRefreshWrapper( +export function addRefreshWrapper( code: string, - map: M | string | typeof avoidSourceMapOption, pluginName: string, id: string, reactRefreshHost = '', -): { code: string; map: M | null | string } { +): string | undefined { const hasRefresh = refreshContentRE.test(code) const onlyReactComp = !hasRefresh && reactCompRE.test(code) - const normalizedMap = map === avoidSourceMapOption ? null : map - - if (!hasRefresh && !onlyReactComp) return { code, map: normalizedMap } - const avoidSourceMap = map === avoidSourceMapOption - const newMap = - typeof normalizedMap === 'string' - ? (JSON.parse(normalizedMap) as M) - : normalizedMap + if (!hasRefresh && !onlyReactComp) return undefined let newCode = code - if (hasRefresh) { - const refreshHead = removeLineBreaksIfNeeded( - `let prevRefreshReg; -let prevRefreshSig; + newCode += ` +import * as RefreshRuntime from "${reactRefreshHost}${runtimePublicPath}"; +const inWebWorker = typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope; if (import.meta.hot && !inWebWorker) { if (!window.$RefreshReg$) { throw new Error( @@ -48,39 +37,6 @@ if (import.meta.hot && !inWebWorker) { ); } - prevRefreshReg = window.$RefreshReg$; - prevRefreshSig = window.$RefreshSig$; - window.$RefreshReg$ = RefreshRuntime.getRefreshReg(${JSON.stringify(id)}); - window.$RefreshSig$ = RefreshRuntime.createSignatureFunctionForTransform; -} - -`, - avoidSourceMap, - ) - - newCode = `${refreshHead}${newCode} - -if (import.meta.hot && !inWebWorker) { - window.$RefreshReg$ = prevRefreshReg; - window.$RefreshSig$ = prevRefreshSig; -} -` - if (newMap) { - newMap.mappings = ';'.repeat(16) + newMap.mappings - } - } - - const sharedHead = removeLineBreaksIfNeeded( - `import * as RefreshRuntime from "${reactRefreshHost}${runtimePublicPath}"; -const inWebWorker = typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope; - -`, - avoidSourceMap, - ) - - newCode = `${sharedHead}${newCode} - -if (import.meta.hot && !inWebWorker) { RefreshRuntime.__hmr_import(import.meta.url).then((currentExports) => { RefreshRuntime.registerExportsForReactRefresh(${JSON.stringify( id, @@ -95,13 +51,12 @@ if (import.meta.hot && !inWebWorker) { }); } ` - if (newMap) { - newMap.mappings = ';;;' + newMap.mappings - } - return { code: newCode, map: newMap } -} + if (hasRefresh) { + newCode += `function $RefreshReg$(type, id) { return RefreshRuntime.register(type, ${JSON.stringify(id)} + ' ' + id) } +function $RefreshSig$() { return RefreshRuntime.createSignatureFunctionForTransform(); } +` + } -function removeLineBreaksIfNeeded(code: string, enabled: boolean): string { - return enabled ? code.replace(/\n/g, '') : code + return newCode } diff --git a/packages/plugin-react-oxc/CHANGELOG.md b/packages/plugin-react-oxc/CHANGELOG.md index 26e1b45c0..0ff9a9285 100644 --- a/packages/plugin-react-oxc/CHANGELOG.md +++ b/packages/plugin-react-oxc/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +### Perf: simplify refresh wrapper generation ([#835](https://github.com/vitejs/vite-plugin-react/pull/835)) + ## 0.4.1 (2025-08-19) ### Set `optimizeDeps.rollupOptions.transform.jsx` instead of `optimizeDeps.rollupOptions.jsx` ([#735](https://github.com/vitejs/vite-plugin-react/pull/735)) diff --git a/packages/plugin-react-oxc/src/index.ts b/packages/plugin-react-oxc/src/index.ts index 86e8d0dc4..e42b7356a 100644 --- a/packages/plugin-react-oxc/src/index.ts +++ b/packages/plugin-react-oxc/src/index.ts @@ -4,7 +4,6 @@ import { readFileSync } from 'node:fs' import type { BuildOptions, Plugin } from 'vite' import { addRefreshWrapper, - avoidSourceMapOption, getPreambleCode, runtimePublicPath, silenceUseClientWarning, @@ -140,13 +139,8 @@ export default function viteReact(opts: Options = {}): Plugin[] { code.includes(jsxImportRuntime)) if (!useFastRefresh) return - const { code: newCode } = addRefreshWrapper( - code, - avoidSourceMapOption, - '@vitejs/plugin-react-oxc', - id, - ) - return { code: newCode, map: null } + const newCode = addRefreshWrapper(code, '@vitejs/plugin-react-oxc', id) + return newCode ? { code: newCode, map: null } : undefined }, }, transformIndexHtml(_, config) { diff --git a/packages/plugin-react-swc/CHANGELOG.md b/packages/plugin-react-swc/CHANGELOG.md index 14c4f2f0a..47d1a83f3 100644 --- a/packages/plugin-react-swc/CHANGELOG.md +++ b/packages/plugin-react-swc/CHANGELOG.md @@ -6,6 +6,8 @@ This is set to `{viteCacheDir}/swc` and override the default of `.swc`. +### Perf: simplify refresh wrapper generation ([#835](https://github.com/vitejs/vite-plugin-react/pull/835)) + ## 4.0.1 (2025-08-19) ### Set `optimizeDeps.rollupOptions.transform.jsx` instead of `optimizeDeps.rollupOptions.jsx` for rolldown-vite ([#735](https://github.com/vitejs/vite-plugin-react/pull/735)) diff --git a/packages/plugin-react-swc/src/index.ts b/packages/plugin-react-swc/src/index.ts index 45b3cad7d..425bac15c 100644 --- a/packages/plugin-react-swc/src/index.ts +++ b/packages/plugin-react-swc/src/index.ts @@ -1,7 +1,6 @@ import { readFileSync } from 'node:fs' import { dirname, join } from 'node:path' import { fileURLToPath } from 'node:url' -import type { SourceMapPayload } from 'node:module' import { createRequire } from 'node:module' import { type JscTarget, @@ -197,13 +196,13 @@ const react = (_options?: Options): Plugin[] => { if (!result) return if (!refresh) return result - return addRefreshWrapper( + const newCode = addRefreshWrapper( result.code, - result.map!, '@vitejs/plugin-react-swc', id, options.reactRefreshHost, ) + return { code: newCode ?? result.code, map: result.map } }, }, options.plugins diff --git a/packages/plugin-react/CHANGELOG.md b/packages/plugin-react/CHANGELOG.md index c8d6e2537..466bcca61 100644 --- a/packages/plugin-react/CHANGELOG.md +++ b/packages/plugin-react/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +### Perf: simplify refresh wrapper generation ([#835](https://github.com/vitejs/vite-plugin-react/pull/835)) + ## 5.0.2 (2025-08-28) ### Skip transform hook completely in rolldown-vite in dev if possible ([#783](https://github.com/vitejs/vite-plugin-react/pull/783)) diff --git a/packages/plugin-react/src/index.ts b/packages/plugin-react/src/index.ts index b8ec8236f..d57708a43 100644 --- a/packages/plugin-react/src/index.ts +++ b/packages/plugin-react/src/index.ts @@ -8,7 +8,6 @@ import * as vite from 'vite' import type { Plugin, ResolvedConfig } from 'vite' import { addRefreshWrapper, - avoidSourceMapOption, getPreambleCode, preambleCode, runtimePublicPath, @@ -340,13 +339,13 @@ export default function viteReact(opts: Options = {}): Plugin[] { if (!useFastRefresh) { return { code: result.code!, map: result.map } } - return addRefreshWrapper( + const code = addRefreshWrapper( result.code!, - result.map!, '@vitejs/plugin-react', id, opts.reactRefreshHost, ) + return { code: code ?? result.code!, map: result.map } } }, }, @@ -376,14 +375,13 @@ export default function viteReact(opts: Options = {}): Plugin[] { code.includes(jsxImportRuntime)) if (!useFastRefresh) return - const { code: newCode } = addRefreshWrapper( + const newCode = addRefreshWrapper( code, - avoidSourceMapOption, '@vitejs/plugin-react', id, opts.reactRefreshHost, ) - return { code: newCode, map: null } + return newCode ? { code: newCode, map: null } : undefined }, }, }