@@ -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 : [ / ^ v i r t u a l : r e a c t - r e f r e s h / ] ,
352- } ,
353- } ,
354- handler : ( source ) => '\0' + source ,
355- } ,
356- load : {
357- filter : {
358- id : {
359- include : [ / ^ \0 v i r t u a l : r e a c t - r e f r e s h / ] ,
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 ( / \b e x p o r t s \. / 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+ }
0 commit comments