Skip to content

Commit

Permalink
feat: update refresh utils for React Router 7 support (#363)
Browse files Browse the repository at this point in the history
Co-authored-by: 翠 / green <green@sapphi.red>
  • Loading branch information
ArnaudBarre and sapphi-red authored Sep 29, 2024
1 parent d88c581 commit 4544e97
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 18 deletions.
16 changes: 9 additions & 7 deletions packages/plugin-react/src/fast-refresh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,17 @@ if (import.meta.hot && !inWebWorker) {
window.$RefreshReg$ = prevRefreshReg;
window.$RefreshSig$ = prevRefreshSig;
}`
const sharedFooter = `
const sharedFooter = (id: string) => `
if (import.meta.hot && !inWebWorker) {
RefreshRuntime.__hmr_import(import.meta.url).then((currentExports) => {
RefreshRuntime.registerExportsForReactRefresh(__SOURCE__, currentExports);
RefreshRuntime.registerExportsForReactRefresh(${JSON.stringify(
id,
)}, currentExports);
import.meta.hot.accept((nextExports) => {
if (!nextExports) return;
const invalidateMessage = RefreshRuntime.validateRefreshBoundaryAndEnqueueUpdate(currentExports, nextExports);
const invalidateMessage = RefreshRuntime.validateRefreshBoundaryAndEnqueueUpdate(${JSON.stringify(
id,
)}, currentExports, nextExports);
if (invalidateMessage) import.meta.hot.invalidate(invalidateMessage);
});
});
Expand All @@ -76,15 +80,13 @@ export function addRefreshWrapper(code: string, id: string): string {
functionHeader.replace('__SOURCE__', JSON.stringify(id)) +
code +
functionFooter +
sharedFooter.replace('__SOURCE__', JSON.stringify(id))
sharedFooter(id)
)
}

export function addClassComponentRefreshWrapper(
code: string,
id: string,
): string {
return (
sharedHeader + code + sharedFooter.replace('__SOURCE__', JSON.stringify(id))
)
return sharedHeader + code + sharedFooter(id)
}
40 changes: 31 additions & 9 deletions packages/plugin-react/src/refreshUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,14 @@ function debounce(fn, delay) {
}

/* eslint-disable no-undef */
const enqueueUpdate = debounce(exports.performReactRefresh, 16)
const hooks = []
window.__registerBeforePerformReactRefresh = (cb) => {
hooks.push(cb)
}
const enqueueUpdate = debounce(async () => {
if (hooks.length) await Promise.all(hooks.map((cb) => cb()))
exports.performReactRefresh()
}, 16)

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

function validateRefreshBoundaryAndEnqueueUpdate(prevExports, nextExports) {
if (!predicateOnExport(prevExports, (key) => key in nextExports)) {
function validateRefreshBoundaryAndEnqueueUpdate(id, prevExports, nextExports) {
const ignoredExports = window.__getReactRefreshIgnoredExports?.({ id }) ?? []
if (
predicateOnExport(
ignoredExports,
prevExports,
(key) => key in nextExports,
) !== true
) {
return 'Could not Fast Refresh (export removed)'
}
if (!predicateOnExport(nextExports, (key) => key in prevExports)) {
if (
predicateOnExport(
ignoredExports,
nextExports,
(key) => key in prevExports,
) !== true
) {
return 'Could not Fast Refresh (new export)'
}

let hasExports = false
const allExportsAreComponentsOrUnchanged = predicateOnExport(
ignoredExports,
nextExports,
(key, value) => {
hasExports = true
if (exports.isLikelyComponentType(value)) return true
return prevExports[key] === nextExports[key]
},
)
if (hasExports && allExportsAreComponentsOrUnchanged) {
if (hasExports && allExportsAreComponentsOrUnchanged === true) {
enqueueUpdate()
} else {
return 'Could not Fast Refresh. Learn more at https://github.com/vitejs/vite-plugin-react/tree/main/packages/plugin-react#consistent-components-exports'
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`
}
}

function predicateOnExport(moduleExports, predicate) {
function predicateOnExport(ignoredExports, moduleExports, predicate) {
for (const key in moduleExports) {
if (key === '__esModule') continue
if (ignoredExports.includes(key)) continue
const desc = Object.getOwnPropertyDescriptor(moduleExports, key)
if (desc && desc.get) return false
if (!predicate(key, moduleExports[key])) return false
if (desc && desc.get) return key
if (!predicate(key, moduleExports[key])) return key
}
return true
}
Expand Down
4 changes: 2 additions & 2 deletions playground/react/__tests__/react.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ if (!isBuild) {
code.replace('An Object', 'Updated'),
),
[
'[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',
'[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',
'[vite] hot updated: /hmr/no-exported-comp.jsx',
'[vite] hot updated: /hmr/parent.jsx',
'Parent rendered',
Expand All @@ -103,7 +103,7 @@ if (!isBuild) {
code.replace('context provider', 'context provider updated'),
),
[
'[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',
'[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',
'[vite] hot updated: /context/CountProvider.jsx',
'[vite] hot updated: /App.jsx',
'[vite] hot updated: /context/ContextButton.jsx',
Expand Down

0 comments on commit 4544e97

Please sign in to comment.