Skip to content

Commit 7977aeb

Browse files
committed
fix(rsc): pre-include React peers in client env optimizeDeps
The client environment only had `react-dom/client` and the React Server DOM browser entry in `optimizeDeps.include`. React itself and crawled React-peer packages were discovered lazily as the browser requested modules, which re-runs the optimizer mid-load and changes the `?v=` hash on already-served chunks. The browser ends up with two distinct React module records, and `'use client'` libraries that synchronously call hooks in providers hit the wrong dispatcher (`React.H` is null), throwing `Invalid hook call`. Mirror the SSR/RSC env behaviour by including `react`, `react-dom`, `react/jsx-runtime`, `react/jsx-dev-runtime`, plus the React-peer packages from `crawlFrameworkPkgs`. Filter that list to packages with a client-resolvable entry so server-only React peers (e.g. `@vitejs/test-dep-client-in-server`) don't break dep optimization.
1 parent 65d378f commit 7977aeb

1 file changed

Lines changed: 23 additions & 0 deletions

File tree

packages/plugin-rsc/src/plugin.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,20 @@ function resolvePackage(name: string) {
111111
return pathToFileURL(require.resolve(name)).href
112112
}
113113

114+
function filterClientResolvable(packages: string[]): string[] {
115+
return packages.filter((name) => {
116+
try {
117+
const pkgJson = require(`${name}/package.json`)
118+
if (pkgJson.exports && typeof pkgJson.exports === 'object') {
119+
return '.' in pkgJson.exports
120+
}
121+
return Boolean(pkgJson.exports || pkgJson.main || pkgJson.module)
122+
} catch {
123+
return false
124+
}
125+
})
126+
}
127+
114128
export type { RscPluginManager }
115129

116130
/**
@@ -517,8 +531,17 @@ export default function vitePluginRsc(
517531
},
518532
optimizeDeps: {
519533
include: [
534+
'react',
535+
'react-dom',
536+
'react/jsx-runtime',
537+
'react/jsx-dev-runtime',
520538
'react-dom/client',
521539
`${reactServerDomPackageName}/client.browser`,
540+
// Pre-discover React-peer packages with a client-resolvable
541+
// entry. Without this, the client optimizer re-scans as
542+
// boundaries are discovered and emits multiple `?v=` hashes
543+
// on cold start, duplicating React in the browser.
544+
...filterClientResolvable(result.ssr.noExternal),
522545
],
523546
exclude: [PKG_NAME],
524547
},

0 commit comments

Comments
 (0)