Skip to content

Commit c5b6026

Browse files
committed
refactor: inline react-refresh/runtime
1 parent 1fc10b5 commit c5b6026

File tree

3 files changed

+57
-76
lines changed

3 files changed

+57
-76
lines changed

packages/vite/src/node/plugins/html.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -734,10 +734,6 @@ export function buildHtmlPlugin(config: ResolvedConfig): RolldownPlugin {
734734

735735
await Promise.all(setModuleSideEffectPromises)
736736

737-
if (config.experimental.rolldownDev?.reactRefresh) {
738-
js = `import "virtual:react-refresh/entry";\n${js}`
739-
}
740-
741737
// Force rollup to keep this module from being shared between other entry points.
742738
// If the resulting chunk is empty, it will be removed in generateBundle.
743739
return { code: js, moduleSideEffects: 'no-treeshake' }

packages/vite/src/node/server/environments/rolldown.ts

Lines changed: 57 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -202,8 +202,8 @@ class RolldownEnvironment extends DevEnvironment {
202202
},
203203
plugins: [
204204
...plugins,
205-
patchRuntimePlugin(),
206-
reactRefreshPlugin(this.rolldownDevOptions),
205+
patchRuntimePlugin(this.rolldownDevOptions),
206+
reactRefreshPlugin(),
207207
],
208208
}
209209
this.instance = await rolldown.rolldown(inputOptions)
@@ -257,7 +257,9 @@ class RolldownEnvironment extends DevEnvironment {
257257
}
258258
}
259259

260-
function patchRuntimePlugin(): rolldown.Plugin {
260+
function patchRuntimePlugin(
261+
rolldownDevOptions: RolldownDevOptions,
262+
): rolldown.Plugin {
261263
return {
262264
name: 'vite:rolldown-patch-runtime',
263265
renderChunk(code) {
@@ -278,6 +280,9 @@ function patchRuntimePlugin(): rolldown.Plugin {
278280
}
279281
for (var i = 0; i < module.parents.length; i++) {`,
280282
)
283+
if (rolldownDevOptions.reactRefresh) {
284+
output.prepend(getReactRefreshRuntimeCode())
285+
}
281286
return { code: output.toString(), map: output.generateMap() }
282287
}
283288
},
@@ -320,82 +325,64 @@ window.__rolldown_hot = hot;
320325
return `(() => {/*** @vite/client ***/\n${code}}\n)()`
321326
}
322327

323-
// TODO: workaround rolldownExperimental.reactPlugin which injects js to html via `load` hook
324-
// TODO: can we inject to "react" itself?
325-
function reactRefreshPlugin(
326-
rolldownDevOptions: RolldownDevOptions,
327-
): rolldown.Plugin {
328+
function reactRefreshPlugin(): rolldown.Plugin {
328329
return {
329-
name: 'react-hmr',
330+
name: 'vite:rolldown-react-refresh',
330331
transform: {
331332
filter: {
332333
code: {
333334
include: ['$RefreshReg$'],
334335
},
335336
},
336337
handler(code, id) {
337-
const output = new MagicString(code)
338-
output.prepend(`
339-
import * as __$refresh from 'virtual:react-refresh';
340-
const [$RefreshSig$, $RefreshReg$] = __$refresh.create(${JSON.stringify(id)});
341-
`)
342-
output.append(`
343-
__$refresh.setupHot(module.hot);
344-
`)
345-
return { code: output.toString(), map: output.generateMap() }
346-
},
347-
},
348-
resolveId: {
349-
filter: {
350-
id: {
351-
include: [/^virtual:react-refresh/],
352-
},
353-
},
354-
handler: (source) => '\0' + source,
355-
},
356-
load: {
357-
filter: {
358-
id: {
359-
include: [/^\0virtual:react-refresh/],
360-
},
361-
},
362-
async handler(id) {
363-
if (!rolldownDevOptions.reactRefresh) {
364-
return `export {}`
365-
}
366-
const resolved = require.resolve('react-refresh/runtime')
367-
if (id === '\0virtual:react-refresh/entry') {
368-
return `
369-
import runtime from ${JSON.stringify(resolved)};
370-
runtime.injectIntoGlobalHook(window);
371-
`
372-
}
373-
if (id === '\0virtual:react-refresh') {
374-
return `
375-
import runtime from ${JSON.stringify(resolved)};
376-
377-
export const create = (file) => [
378-
runtime.createSignatureFunctionForTransform,
379-
(type, id) => runtime.register(type, file + '_' + id),
380-
];
381-
382-
function debounce(fn, delay) {
383-
let handle
384-
return () => {
385-
clearTimeout(handle)
386-
handle = setTimeout(fn, delay)
387-
}
388-
}
389-
const debouncedRefresh = debounce(runtime.performReactRefresh, 16);
390-
391-
export function setupHot(hot) {
392-
hot.accept((prev) => {
393-
debouncedRefresh();
394-
});
395-
}
396-
`
397-
}
338+
return [
339+
`const [$RefreshSig$, $RefreshReg$] = __react_refresh_transform_define(${JSON.stringify(id)})`,
340+
code,
341+
`__react_refresh_transform_setupHot(module.hot)`,
342+
].join(';')
398343
},
399344
},
400345
}
401346
}
347+
348+
// inject react refresh runtime in client runtime to ensure initialized early
349+
function getReactRefreshRuntimeCode() {
350+
const code = fs.readFileSync(
351+
path.resolve(
352+
require.resolve('react-refresh/runtime'),
353+
'..',
354+
'cjs/react-refresh-runtime.development.js',
355+
),
356+
'utf-8',
357+
)
358+
const output = new MagicString(code)
359+
output.prepend('self.__react_refresh_runtime = {};\n')
360+
output.replaceAll('process.env.NODE_ENV !== "production"', 'true')
361+
output.replaceAll(/\bexports\./g, '__react_refresh_runtime.')
362+
output.append(`
363+
(() => {
364+
__react_refresh_runtime.injectIntoGlobalHook(self);
365+
366+
__react_refresh_transform_define = (file) => [
367+
__react_refresh_runtime.createSignatureFunctionForTransform,
368+
(type, id) => __react_refresh_runtime.register(type, file + '_' + id)
369+
];
370+
371+
__react_refresh_transform_setupHot = (hot) => {
372+
hot.accept((prev) => {
373+
debouncedRefresh();
374+
});
375+
};
376+
377+
function debounce(fn, delay) {
378+
let handle
379+
return () => {
380+
clearTimeout(handle)
381+
handle = setTimeout(fn, delay)
382+
}
383+
}
384+
const debouncedRefresh = debounce(__react_refresh_runtime.performReactRefresh, 16);
385+
})()
386+
`)
387+
return output.toString()
388+
}

playground/rolldown-dev-ssr/src/entry-client.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
// TODO: how to inject automatically?
2-
import 'virtual:react-refresh/entry'
31
import React from 'react'
42
import ReactDOMClient from 'react-dom/client'
53
import { App } from './app'

0 commit comments

Comments
 (0)