Skip to content

Commit b32947c

Browse files
committed
fast refresh
1 parent c83196d commit b32947c

File tree

3 files changed

+80
-0
lines changed

3 files changed

+80
-0
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Must run before React loads. Creates __REACT_DEVTOOLS_GLOBAL_HOOK__ so
2+
// React's renderer injects into it, enabling react-refresh to work.
3+
if (typeof window !== 'undefined' && !window.__REACT_DEVTOOLS_GLOBAL_HOOK__) {
4+
var nextID = 0;
5+
window.__REACT_DEVTOOLS_GLOBAL_HOOK__ = {
6+
renderers: new Map(),
7+
supportsFiber: true,
8+
inject: function (injected) {
9+
var id = nextID++;
10+
this.renderers.set(id, injected);
11+
return id;
12+
},
13+
onScheduleFiberRoot: function () {},
14+
onCommitFiberRoot: function () {},
15+
onCommitFiberUnmount: function () {},
16+
};
17+
}

src/components/MDX/Sandpack/sandpack-rsc/sandbox-code/src/rsc-client.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,17 @@ import {
2424
encodeReply,
2525
} from 'react-server-dom-webpack/client.browser';
2626

27+
import {
28+
injectIntoGlobalHook,
29+
register as refreshRegister,
30+
performReactRefresh,
31+
isLikelyComponentType,
32+
} from 'react-refresh/runtime';
33+
34+
// Patch the DevTools hook to capture renderer helpers and track roots.
35+
// Must run after react-dom evaluates (injects renderer) but before createRoot().
36+
injectIntoGlobalHook(window);
37+
2738
export function initClient() {
2839
// Create Worker from pre-bundled server runtime
2940
var blob = new Blob([rscServerForWorker], {type: 'application/javascript'});
@@ -234,6 +245,7 @@ export function initClient() {
234245
if (filePath === '/src/rsc-client.js') return;
235246
if (filePath === '/src/rsc-server.js') return;
236247
if (filePath === '/src/__webpack_shim__.js') return;
248+
if (filePath === '/src/__react_refresh_init__.js') return;
237249
userFiles[filePath] = files[filePath];
238250
});
239251
worker.postMessage({
@@ -327,5 +339,33 @@ export function initClient() {
327339
Object.keys(clientEntries).forEach(function (moduleId) {
328340
evaluateModule(moduleId);
329341
});
342+
343+
// Register all evaluated components with react-refresh for Fast Refresh.
344+
// This creates stable "component families" so React can preserve state
345+
// across re-evaluations when component identity changes.
346+
Object.keys(globalThis.__webpack_module_cache__).forEach(function (
347+
moduleId
348+
) {
349+
var moduleExports = globalThis.__webpack_module_cache__[moduleId];
350+
var exports =
351+
moduleExports.exports !== undefined
352+
? moduleExports.exports
353+
: moduleExports;
354+
if (exports && typeof exports === 'object') {
355+
for (var key in exports) {
356+
var exportValue = exports[key];
357+
if (isLikelyComponentType(exportValue)) {
358+
refreshRegister(exportValue, moduleId + ' %exports% ' + key);
359+
}
360+
}
361+
}
362+
if (typeof exports === 'function' && isLikelyComponentType(exports)) {
363+
refreshRegister(exports, moduleId + ' %exports% default');
364+
}
365+
});
366+
367+
// Tell React about updated component families so it can
368+
// preserve state for components whose implementation changed.
369+
performReactRefresh();
330370
}
331371
}

src/components/MDX/Sandpack/templateRSC.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,34 @@ const RSC_SOURCE_FILES = {
2222
require('!raw-loader?esModule=false!./sandpack-rsc/sandbox-code/src/webpack-shim.js') as string,
2323
'rsc-client':
2424
require('!raw-loader?esModule=false!./sandpack-rsc/sandbox-code/src/rsc-client.js') as string,
25+
'react-refresh-init':
26+
require('!raw-loader?esModule=false!./sandpack-rsc/sandbox-code/src/__react_refresh_init__.js') as string,
2527
'worker-bundle': `export default ${JSON.stringify(
2628
require('!raw-loader?esModule=false!./sandpack-rsc/sandbox-code/src/worker-bundle.dist.js') as string
2729
)};`,
2830
'rsdw-client':
2931
require('!raw-loader?esModule=false!../../../../node_modules/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.production.js') as string,
3032
};
3133

34+
// Load react-refresh runtime and strip the process.env.NODE_ENV guard
35+
// so it works in Sandpack's bundler which may not replace process.env.
36+
const reactRefreshRaw =
37+
require('!raw-loader?esModule=false!../../../../node_modules/next/dist/compiled/react-refresh/cjs/react-refresh-runtime.development.js') as string;
38+
39+
// Wrap as a CJS module that Sandpack can require.
40+
// Strip the `if (process.env.NODE_ENV !== "production")` guard so the
41+
// runtime always executes inside the sandbox.
42+
const reactRefreshModule = reactRefreshRaw.replace(
43+
/if \(process\.env\.NODE_ENV !== "production"\) \{/,
44+
'{'
45+
);
46+
3247
// Entry point that bootstraps the RSC client pipeline.
48+
// __react_refresh_init__ must be imported BEFORE rsc-client so the
49+
// DevTools hook stub exists before React's renderer loads.
3350
const indexEntry = `
3451
import './styles.css';
52+
import './__react_refresh_init__';
3553
import { initClient } from './rsc-client.js';
3654
initClient();
3755
`.trim();
@@ -54,6 +72,7 @@ export const templateRSC: SandpackFiles = {
5472
...hideFiles({
5573
'/public/index.html': indexHTML,
5674
'/src/index.js': indexEntry,
75+
'/src/__react_refresh_init__.js': RSC_SOURCE_FILES['react-refresh-init'],
5776
'/src/rsc-client.js': RSC_SOURCE_FILES['rsc-client'],
5877
'/src/rsc-server.js': RSC_SOURCE_FILES['worker-bundle'],
5978
'/src/__webpack_shim__.js': RSC_SOURCE_FILES['webpack-shim'],
@@ -62,6 +81,10 @@ export const templateRSC: SandpackFiles = {
6281
'{"name":"react-server-dom-webpack","main":"index.js"}',
6382
'/node_modules/react-server-dom-webpack/client.browser.js':
6483
RSC_SOURCE_FILES['rsdw-client'],
84+
// react-refresh runtime as a Sandpack local dependency
85+
'/node_modules/react-refresh/package.json':
86+
'{"name":"react-refresh","main":"runtime.js"}',
87+
'/node_modules/react-refresh/runtime.js': reactRefreshModule,
6588
'/package.json': JSON.stringify(
6689
{
6790
name: 'react.dev',

0 commit comments

Comments
 (0)