Skip to content

Commit bfb1738

Browse files
authored
fix: react refresh runtime injection (#2328)
1 parent 2934f8f commit bfb1738

File tree

6 files changed

+69
-53
lines changed

6 files changed

+69
-53
lines changed

.changeset/polite-tigers-obey.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@rspack/dev-client": patch
3+
"@rspack/dev-middleware": patch
4+
"@rspack/dev-server": patch
5+
---
6+
7+
fix: react refresh runtime injection

crates/rspack_plugin_javascript/src/visitors/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ pub fn run_before_pass(
9898
let uri = resource_data.resource.as_str();
9999
swc_visitor::fold_react_refresh(context, uri)
100100
},
101-
should_transform_by_react
101+
should_transform_by_react && options.builtins.react.refresh.is_some()
102102
),
103103
either!(
104104
options.builtins.emotion,
Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,22 @@
11
// @ts-ignore
22
const RefreshRuntime = require("react-refresh/runtime");
33

4-
if (process.env.NODE_ENV !== "production") {
5-
function debounce(fn: Function, delay: number) {
6-
var handle: number | undefined;
7-
return () => {
8-
clearTimeout(handle);
9-
handle = setTimeout(fn, delay);
10-
};
11-
}
4+
function debounce(fn: Function, delay: number) {
5+
var handle: number | undefined;
6+
return () => {
7+
clearTimeout(handle);
8+
handle = setTimeout(fn, delay);
9+
};
10+
}
1211

13-
RefreshRuntime.injectIntoGlobalHook(globalThis);
14-
globalThis.$RefreshReg$ = () => {};
15-
globalThis.$RefreshSig$ = () => type => type;
12+
RefreshRuntime.injectIntoGlobalHook(globalThis);
13+
globalThis.$RefreshReg$ = () => {};
14+
globalThis.$RefreshSig$ = () => type => type;
1615

17-
var queueUpdate = debounce(RefreshRuntime.performReactRefresh, 16);
16+
var queueUpdate = debounce(RefreshRuntime.performReactRefresh, 16);
1817

19-
// @ts-ignored
20-
__webpack_modules__.$ReactRefreshRuntime$ = {
21-
queueUpdate,
22-
...RefreshRuntime
23-
};
24-
} else {
25-
throw Error(
26-
"React Refresh runtime should not be included in the production bundle."
27-
);
28-
}
18+
// @ts-ignored
19+
__webpack_modules__.$ReactRefreshRuntime$ = {
20+
queueUpdate,
21+
...RefreshRuntime
22+
};

packages/rspack-dev-server/src/server.ts

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,19 @@ export class RspackDevServer extends WebpackDevServer {
3636
const additionalEntries: string[] = [];
3737
// @ts-expect-error
3838
const isWebTarget = WebpackDevServer.isWebTarget(compiler);
39+
// inject runtime first, avoid other additional entry after transfrom depend on it
40+
if (this.options.hot) {
41+
if (compiler.options.builtins.react?.refresh) {
42+
const reactRefreshEntryPath = require.resolve(
43+
"@rspack/dev-client/react-refresh"
44+
);
45+
additionalEntries.push(reactRefreshEntryPath);
46+
}
47+
const hotUpdateEntryPath = require.resolve(
48+
"@rspack/dev-client/devServer"
49+
);
50+
additionalEntries.push(hotUpdateEntryPath);
51+
}
3952
if (this.options.client && isWebTarget) {
4053
let webSocketURLStr = "";
4154

@@ -187,20 +200,6 @@ export class RspackDevServer extends WebpackDevServer {
187200
);
188201
}
189202

190-
if (this.options.hot) {
191-
const hotUpdateEntryPath = require.resolve(
192-
"@rspack/dev-client/devServer"
193-
);
194-
additionalEntries.push(hotUpdateEntryPath);
195-
196-
if (compiler.options.builtins.react?.refresh) {
197-
const reactRefreshEntryPath = require.resolve(
198-
"@rspack/dev-client/react-refresh"
199-
);
200-
additionalEntries.push(reactRefreshEntryPath);
201-
}
202-
}
203-
204203
for (const key in compiler.options.entry) {
205204
compiler.options.entry[key].import.unshift(...additionalEntries);
206205
}
@@ -262,10 +261,9 @@ export class RspackDevServer extends WebpackDevServer {
262261
}
263262
if (!clientImplementationFound) {
264263
throw new Error(
265-
`${
266-
!isKnownWebSocketServerImplementation
267-
? "When you use custom web socket implementation you must explicitly specify client.webSocketTransport. "
268-
: ""
264+
`${!isKnownWebSocketServerImplementation
265+
? "When you use custom web socket implementation you must explicitly specify client.webSocketTransport. "
266+
: ""
269267
}client.webSocketTransport must be a string denoting a default implementation (e.g. 'sockjs', 'ws') or a full path to a JS file via require.resolve(...) which exports a class `
270268
);
271269
}
@@ -280,16 +278,30 @@ export class RspackDevServer extends WebpackDevServer {
280278
: [this.compiler];
281279

282280
compilers.forEach(compiler => {
281+
const mode = compiler.options.mode || process.env.NODE_ENV;
283282
if (this.options.hot) {
283+
if (mode === "production") {
284+
this.logger.warn(
285+
"Hot Module Replacement (HMR) is enabled for the production build. \n" +
286+
"Make sure to disable HMR for production by setting `devServer.hot` to `false` in the configuration."
287+
);
288+
}
284289
compiler.options.devServer ??= {};
285290
compiler.options.devServer.hot = true;
286291
compiler.options.builtins.react ??= {};
287292
compiler.options.builtins.react.refresh ??= true;
288293
compiler.options.builtins.react.development ??= true;
289294
} else if (compiler.options.builtins.react.refresh) {
290-
this.logger.warn(
291-
"builtins.react.refresh needs builtins.react.development and devServer.hot enabled"
292-
);
295+
if (mode === "production") {
296+
this.logger.warn(
297+
"React Refresh runtime should not be included in the production bundle.\n" +
298+
"Make sure to disable React Refresh for production by setting `builtins.react.refresh` to `false` in the configuration."
299+
);
300+
} else {
301+
this.logger.warn(
302+
"The `builtins.react.refresh` needs `builtins.react.development` and `devServer.hot` enabled"
303+
);
304+
}
293305
}
294306
});
295307

packages/rspack-dev-server/tests/__snapshots__/normalizeOptions.test.ts.snap

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
exports[`normalize options snapshot additional entires should added 1`] = `
44
{
55
"main": [
6-
"<prefix>/dist/clients/WebSocketClient.js",
7-
"<prefix>/rspack-dev-client/dist/index.js?protocol=ws%3A&hostname=0.0.0.0&port=8080&pathname=%2Fws&logging=info&overlay=true&reconnect=10&hot=true&live-reload=true",
8-
"<prefix>/rspack-dev-client/dist/devServer.js",
96
"<prefix>/rspack-dev-client/dist/reactRefresh.js",
7+
"<prefix>/rspack-dev-client/dist/devServer.js",
8+
"<prefix>/dist/clients/WebSocketClient.js",
9+
"<prefix>/rspack-dev-client/dist/index.js?protocol=ws%3A&hostname=0.0.0.0&&pathname=%2Fws&logging=info&overlay=true&reconnect=10&hot=true&live-reload=true",
1010
"<prefix>/./placeholder.js",
1111
],
1212
}
@@ -30,7 +30,6 @@ exports[`normalize options snapshot no options 1`] = `
3030
"liveReload": true,
3131
"magicHtml": true,
3232
"open": [],
33-
"port": 8080,
3433
"server": {
3534
"options": {},
3635
"type": "http",
@@ -87,7 +86,6 @@ exports[`normalize options snapshot port string 1`] = `
8786
"liveReload": true,
8887
"magicHtml": true,
8988
"open": [],
90-
"port": 9000,
9189
"server": {
9290
"options": {},
9391
"type": "http",
@@ -129,10 +127,10 @@ exports[`normalize options snapshot port string 1`] = `
129127
exports[`normalize options snapshot react-refresh client added when react/refresh enabled 1`] = `
130128
{
131129
"main": [
132-
"<prefix>/dist/clients/WebSocketClient.js",
133-
"<prefix>/rspack-dev-client/dist/index.js?protocol=ws%3A&hostname=0.0.0.0&port=8080&pathname=%2Fws&logging=info&overlay=true&reconnect=10&hot=true&live-reload=true",
134-
"<prefix>/rspack-dev-client/dist/devServer.js",
135130
"<prefix>/rspack-dev-client/dist/reactRefresh.js",
131+
"<prefix>/rspack-dev-client/dist/devServer.js",
132+
"<prefix>/dist/clients/WebSocketClient.js",
133+
"<prefix>/rspack-dev-client/dist/index.js?protocol=ws%3A&hostname=0.0.0.0&&pathname=%2Fws&logging=info&overlay=true&reconnect=10&hot=true&live-reload=true",
136134
"<prefix>/./placeholder.js",
137135
],
138136
}

packages/rspack-dev-server/tests/normalizeOptions.test.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ async function match(config: RspackOptions) {
9494
compiler
9595
);
9696
await server.start();
97+
// it will break ci
98+
delete server.options.port;
9799
expect(server.options).toMatchSnapshot();
98100
await server.stop();
99101
}
@@ -121,7 +123,10 @@ async function matchAdditionEntries(
121123
const value = Object.fromEntries(
122124
entires.map(([key, item]) => {
123125
const replaced = item.import?.map(entry => {
124-
const array = entry.replace(/\\/g, "/").split("/");
126+
const array = entry
127+
.replace(/\\/g, "/")
128+
.replace(/port=\d+/g, "")
129+
.split("/");
125130
return "<prefix>" + "/" + array.slice(-3).join("/");
126131
});
127132
return [key, replaced];

0 commit comments

Comments
 (0)