Skip to content

Commit 4544e97

Browse files
feat: update refresh utils for React Router 7 support (#363)
Co-authored-by: 翠 / green <green@sapphi.red>
1 parent d88c581 commit 4544e97

File tree

3 files changed

+42
-18
lines changed

3 files changed

+42
-18
lines changed

packages/plugin-react/src/fast-refresh.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,17 @@ if (import.meta.hot && !inWebWorker) {
5858
window.$RefreshReg$ = prevRefreshReg;
5959
window.$RefreshSig$ = prevRefreshSig;
6060
}`
61-
const sharedFooter = `
61+
const sharedFooter = (id: string) => `
6262
if (import.meta.hot && !inWebWorker) {
6363
RefreshRuntime.__hmr_import(import.meta.url).then((currentExports) => {
64-
RefreshRuntime.registerExportsForReactRefresh(__SOURCE__, currentExports);
64+
RefreshRuntime.registerExportsForReactRefresh(${JSON.stringify(
65+
id,
66+
)}, currentExports);
6567
import.meta.hot.accept((nextExports) => {
6668
if (!nextExports) return;
67-
const invalidateMessage = RefreshRuntime.validateRefreshBoundaryAndEnqueueUpdate(currentExports, nextExports);
69+
const invalidateMessage = RefreshRuntime.validateRefreshBoundaryAndEnqueueUpdate(${JSON.stringify(
70+
id,
71+
)}, currentExports, nextExports);
6872
if (invalidateMessage) import.meta.hot.invalidate(invalidateMessage);
6973
});
7074
});
@@ -76,15 +80,13 @@ export function addRefreshWrapper(code: string, id: string): string {
7680
functionHeader.replace('__SOURCE__', JSON.stringify(id)) +
7781
code +
7882
functionFooter +
79-
sharedFooter.replace('__SOURCE__', JSON.stringify(id))
83+
sharedFooter(id)
8084
)
8185
}
8286

8387
export function addClassComponentRefreshWrapper(
8488
code: string,
8589
id: string,
8690
): string {
87-
return (
88-
sharedHeader + code + sharedFooter.replace('__SOURCE__', JSON.stringify(id))
89-
)
91+
return sharedHeader + code + sharedFooter(id)
9092
}

packages/plugin-react/src/refreshUtils.js

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,14 @@ function debounce(fn, delay) {
77
}
88

99
/* eslint-disable no-undef */
10-
const enqueueUpdate = debounce(exports.performReactRefresh, 16)
10+
const hooks = []
11+
window.__registerBeforePerformReactRefresh = (cb) => {
12+
hooks.push(cb)
13+
}
14+
const enqueueUpdate = debounce(async () => {
15+
if (hooks.length) await Promise.all(hooks.map((cb) => cb()))
16+
exports.performReactRefresh()
17+
}, 16)
1118

1219
// Taken from https://github.com/pmmmwh/react-refresh-webpack-plugin/blob/main/lib/runtime/RefreshUtils.js#L141
1320
// This allows to resister components not detected by SWC like styled component
@@ -25,36 +32,51 @@ function registerExportsForReactRefresh(filename, moduleExports) {
2532
}
2633
}
2734

28-
function validateRefreshBoundaryAndEnqueueUpdate(prevExports, nextExports) {
29-
if (!predicateOnExport(prevExports, (key) => key in nextExports)) {
35+
function validateRefreshBoundaryAndEnqueueUpdate(id, prevExports, nextExports) {
36+
const ignoredExports = window.__getReactRefreshIgnoredExports?.({ id }) ?? []
37+
if (
38+
predicateOnExport(
39+
ignoredExports,
40+
prevExports,
41+
(key) => key in nextExports,
42+
) !== true
43+
) {
3044
return 'Could not Fast Refresh (export removed)'
3145
}
32-
if (!predicateOnExport(nextExports, (key) => key in prevExports)) {
46+
if (
47+
predicateOnExport(
48+
ignoredExports,
49+
nextExports,
50+
(key) => key in prevExports,
51+
) !== true
52+
) {
3353
return 'Could not Fast Refresh (new export)'
3454
}
3555

3656
let hasExports = false
3757
const allExportsAreComponentsOrUnchanged = predicateOnExport(
58+
ignoredExports,
3859
nextExports,
3960
(key, value) => {
4061
hasExports = true
4162
if (exports.isLikelyComponentType(value)) return true
4263
return prevExports[key] === nextExports[key]
4364
},
4465
)
45-
if (hasExports && allExportsAreComponentsOrUnchanged) {
66+
if (hasExports && allExportsAreComponentsOrUnchanged === true) {
4667
enqueueUpdate()
4768
} else {
48-
return 'Could not Fast Refresh. Learn more at https://github.com/vitejs/vite-plugin-react/tree/main/packages/plugin-react#consistent-components-exports'
69+
return `Could not Fast Refresh ("${allExportsAreComponentsOrUnchanged}" export is incompatible). Learn more at https://github.com/vitejs/vite-plugin-react/tree/main/packages/plugin-react#consistent-components-exports`
4970
}
5071
}
5172

52-
function predicateOnExport(moduleExports, predicate) {
73+
function predicateOnExport(ignoredExports, moduleExports, predicate) {
5374
for (const key in moduleExports) {
5475
if (key === '__esModule') continue
76+
if (ignoredExports.includes(key)) continue
5577
const desc = Object.getOwnPropertyDescriptor(moduleExports, key)
56-
if (desc && desc.get) return false
57-
if (!predicate(key, moduleExports[key])) return false
78+
if (desc && desc.get) return key
79+
if (!predicate(key, moduleExports[key])) return key
5880
}
5981
return true
6082
}

playground/react/__tests__/react.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ if (!isBuild) {
7878
code.replace('An Object', 'Updated'),
7979
),
8080
[
81-
'[vite] invalidate /hmr/no-exported-comp.jsx: Could not Fast Refresh. Learn more at https://github.com/vitejs/vite-plugin-react/tree/main/packages/plugin-react#consistent-components-exports',
81+
'[vite] invalidate /hmr/no-exported-comp.jsx: Could not Fast Refresh ("Foo" export is incompatible). Learn more at https://github.com/vitejs/vite-plugin-react/tree/main/packages/plugin-react#consistent-components-exports',
8282
'[vite] hot updated: /hmr/no-exported-comp.jsx',
8383
'[vite] hot updated: /hmr/parent.jsx',
8484
'Parent rendered',
@@ -103,7 +103,7 @@ if (!isBuild) {
103103
code.replace('context provider', 'context provider updated'),
104104
),
105105
[
106-
'[vite] invalidate /context/CountProvider.jsx: Could not Fast Refresh. Learn more at https://github.com/vitejs/vite-plugin-react/tree/main/packages/plugin-react#consistent-components-exports',
106+
'[vite] invalidate /context/CountProvider.jsx: Could not Fast Refresh ("CountContext" export is incompatible). Learn more at https://github.com/vitejs/vite-plugin-react/tree/main/packages/plugin-react#consistent-components-exports',
107107
'[vite] hot updated: /context/CountProvider.jsx',
108108
'[vite] hot updated: /App.jsx',
109109
'[vite] hot updated: /context/ContextButton.jsx',

0 commit comments

Comments
 (0)