Skip to content

Commit 3a8b4a9

Browse files
authored
perf: avoid sourcemap for refresh wrapper injection (#835)
1 parent f63bb83 commit 3a8b4a9

File tree

8 files changed

+27
-79
lines changed

8 files changed

+27
-79
lines changed

packages/common/refresh-runtime.js

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ function performReactRefresh() {
243243
}
244244
}
245245

246-
function register(type, id) {
246+
export function register(type, id) {
247247
if (type === null) {
248248
return
249249
}
@@ -564,10 +564,6 @@ function isPlainObject(obj) {
564564
* Plugin utils
565565
*/
566566

567-
export function getRefreshReg(filename) {
568-
return (type, id) => register(type, filename + ' ' + id)
569-
}
570-
571567
// Taken from https://github.com/pmmmwh/react-refresh-webpack-plugin/blob/main/lib/runtime/RefreshUtils.js#L141
572568
// This allows to resister components not detected by SWC like styled component
573569
export function registerExportsForReactRefresh(filename, moduleExports) {

packages/common/refresh-utils.ts

Lines changed: 12 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -14,73 +14,29 @@ window.$RefreshSig$ = () => (type) => type;`
1414
export const getPreambleCode = (base: string): string =>
1515
preambleCode.replace('__BASE__', base)
1616

17-
export const avoidSourceMapOption = Symbol()
18-
19-
export function addRefreshWrapper<M extends { mappings: string }>(
17+
export function addRefreshWrapper(
2018
code: string,
21-
map: M | string | typeof avoidSourceMapOption,
2219
pluginName: string,
2320
id: string,
2421
reactRefreshHost = '',
25-
): { code: string; map: M | null | string } {
22+
): string | undefined {
2623
const hasRefresh = refreshContentRE.test(code)
2724
const onlyReactComp = !hasRefresh && reactCompRE.test(code)
28-
const normalizedMap = map === avoidSourceMapOption ? null : map
29-
30-
if (!hasRefresh && !onlyReactComp) return { code, map: normalizedMap }
3125

32-
const avoidSourceMap = map === avoidSourceMapOption
33-
const newMap =
34-
typeof normalizedMap === 'string'
35-
? (JSON.parse(normalizedMap) as M)
36-
: normalizedMap
26+
if (!hasRefresh && !onlyReactComp) return undefined
3727

3828
let newCode = code
39-
if (hasRefresh) {
40-
const refreshHead = removeLineBreaksIfNeeded(
41-
`let prevRefreshReg;
42-
let prevRefreshSig;
29+
newCode += `
4330
31+
import * as RefreshRuntime from "${reactRefreshHost}${runtimePublicPath}";
32+
const inWebWorker = typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope;
4433
if (import.meta.hot && !inWebWorker) {
4534
if (!window.$RefreshReg$) {
4635
throw new Error(
4736
"${pluginName} can't detect preamble. Something is wrong."
4837
);
4938
}
5039
51-
prevRefreshReg = window.$RefreshReg$;
52-
prevRefreshSig = window.$RefreshSig$;
53-
window.$RefreshReg$ = RefreshRuntime.getRefreshReg(${JSON.stringify(id)});
54-
window.$RefreshSig$ = RefreshRuntime.createSignatureFunctionForTransform;
55-
}
56-
57-
`,
58-
avoidSourceMap,
59-
)
60-
61-
newCode = `${refreshHead}${newCode}
62-
63-
if (import.meta.hot && !inWebWorker) {
64-
window.$RefreshReg$ = prevRefreshReg;
65-
window.$RefreshSig$ = prevRefreshSig;
66-
}
67-
`
68-
if (newMap) {
69-
newMap.mappings = ';'.repeat(16) + newMap.mappings
70-
}
71-
}
72-
73-
const sharedHead = removeLineBreaksIfNeeded(
74-
`import * as RefreshRuntime from "${reactRefreshHost}${runtimePublicPath}";
75-
const inWebWorker = typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope;
76-
77-
`,
78-
avoidSourceMap,
79-
)
80-
81-
newCode = `${sharedHead}${newCode}
82-
83-
if (import.meta.hot && !inWebWorker) {
8440
RefreshRuntime.__hmr_import(import.meta.url).then((currentExports) => {
8541
RefreshRuntime.registerExportsForReactRefresh(${JSON.stringify(
8642
id,
@@ -95,13 +51,12 @@ if (import.meta.hot && !inWebWorker) {
9551
});
9652
}
9753
`
98-
if (newMap) {
99-
newMap.mappings = ';;;' + newMap.mappings
100-
}
10154

102-
return { code: newCode, map: newMap }
103-
}
55+
if (hasRefresh) {
56+
newCode += `function $RefreshReg$(type, id) { return RefreshRuntime.register(type, ${JSON.stringify(id)} + ' ' + id) }
57+
function $RefreshSig$() { return RefreshRuntime.createSignatureFunctionForTransform(); }
58+
`
59+
}
10460

105-
function removeLineBreaksIfNeeded(code: string, enabled: boolean): string {
106-
return enabled ? code.replace(/\n/g, '') : code
61+
return newCode
10762
}

packages/plugin-react-oxc/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## Unreleased
44

5+
### Perf: simplify refresh wrapper generation ([#835](https://github.com/vitejs/vite-plugin-react/pull/835))
6+
57
## 0.4.1 (2025-08-19)
68

79
### Set `optimizeDeps.rollupOptions.transform.jsx` instead of `optimizeDeps.rollupOptions.jsx` ([#735](https://github.com/vitejs/vite-plugin-react/pull/735))

packages/plugin-react-oxc/src/index.ts

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { readFileSync } from 'node:fs'
44
import type { BuildOptions, Plugin } from 'vite'
55
import {
66
addRefreshWrapper,
7-
avoidSourceMapOption,
87
getPreambleCode,
98
runtimePublicPath,
109
silenceUseClientWarning,
@@ -140,13 +139,8 @@ export default function viteReact(opts: Options = {}): Plugin[] {
140139
code.includes(jsxImportRuntime))
141140
if (!useFastRefresh) return
142141

143-
const { code: newCode } = addRefreshWrapper(
144-
code,
145-
avoidSourceMapOption,
146-
'@vitejs/plugin-react-oxc',
147-
id,
148-
)
149-
return { code: newCode, map: null }
142+
const newCode = addRefreshWrapper(code, '@vitejs/plugin-react-oxc', id)
143+
return newCode ? { code: newCode, map: null } : undefined
150144
},
151145
},
152146
transformIndexHtml(_, config) {

packages/plugin-react-swc/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
This is set to `{viteCacheDir}/swc` and override the default of `.swc`.
88

9+
### Perf: simplify refresh wrapper generation ([#835](https://github.com/vitejs/vite-plugin-react/pull/835))
10+
911
## 4.0.1 (2025-08-19)
1012

1113
### Set `optimizeDeps.rollupOptions.transform.jsx` instead of `optimizeDeps.rollupOptions.jsx` for rolldown-vite ([#735](https://github.com/vitejs/vite-plugin-react/pull/735))

packages/plugin-react-swc/src/index.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { readFileSync } from 'node:fs'
22
import { dirname, join } from 'node:path'
33
import { fileURLToPath } from 'node:url'
4-
import type { SourceMapPayload } from 'node:module'
54
import { createRequire } from 'node:module'
65
import {
76
type JscTarget,
@@ -197,13 +196,13 @@ const react = (_options?: Options): Plugin[] => {
197196
if (!result) return
198197
if (!refresh) return result
199198

200-
return addRefreshWrapper<SourceMapPayload>(
199+
const newCode = addRefreshWrapper(
201200
result.code,
202-
result.map!,
203201
'@vitejs/plugin-react-swc',
204202
id,
205203
options.reactRefreshHost,
206204
)
205+
return { code: newCode ?? result.code, map: result.map }
207206
},
208207
},
209208
options.plugins

packages/plugin-react/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## Unreleased
44

5+
### Perf: simplify refresh wrapper generation ([#835](https://github.com/vitejs/vite-plugin-react/pull/835))
6+
57
## 5.0.2 (2025-08-28)
68

79
### Skip transform hook completely in rolldown-vite in dev if possible ([#783](https://github.com/vitejs/vite-plugin-react/pull/783))

packages/plugin-react/src/index.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import * as vite from 'vite'
88
import type { Plugin, ResolvedConfig } from 'vite'
99
import {
1010
addRefreshWrapper,
11-
avoidSourceMapOption,
1211
getPreambleCode,
1312
preambleCode,
1413
runtimePublicPath,
@@ -340,13 +339,13 @@ export default function viteReact(opts: Options = {}): Plugin[] {
340339
if (!useFastRefresh) {
341340
return { code: result.code!, map: result.map }
342341
}
343-
return addRefreshWrapper(
342+
const code = addRefreshWrapper(
344343
result.code!,
345-
result.map!,
346344
'@vitejs/plugin-react',
347345
id,
348346
opts.reactRefreshHost,
349347
)
348+
return { code: code ?? result.code!, map: result.map }
350349
}
351350
},
352351
},
@@ -376,14 +375,13 @@ export default function viteReact(opts: Options = {}): Plugin[] {
376375
code.includes(jsxImportRuntime))
377376
if (!useFastRefresh) return
378377

379-
const { code: newCode } = addRefreshWrapper(
378+
const newCode = addRefreshWrapper(
380379
code,
381-
avoidSourceMapOption,
382380
'@vitejs/plugin-react',
383381
id,
384382
opts.reactRefreshHost,
385383
)
386-
return { code: newCode, map: null }
384+
return newCode ? { code: newCode, map: null } : undefined
387385
},
388386
},
389387
}

0 commit comments

Comments
 (0)