fix(plugin-react): resolve base-prefixed refresh runtime in bundledDev#1121
fix(plugin-react): resolve base-prefixed refresh runtime in bundledDev#1121Vishnuuuu24 wants to merge 1 commit intovitejs:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR fixes an issue where the React Fast Refresh runtime cannot be resolved in bundledDev mode when a non-root base path is configured. In bundledDev mode with a custom base (e.g., '/ui/'), the preamble code generates imports like '/ui/@react-refresh', but these were not being resolved correctly.
Changes:
- Added a new pre-resolve hook that intercepts base-prefixed refresh runtime imports in bundledDev mode and normalizes them to the canonical path
- Added a regression test to verify the resolve behavior with a custom base path in bundledDev mode
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| packages/plugin-react/src/index.ts | Implements viteReactRefreshBundledDevModeResolveRuntime plugin that maps base-prefixed runtime paths (e.g., '/ui/@react-refresh') back to the canonical path ('/@react-refresh') when Fast Refresh is enabled in bundledDev mode |
| packages/plugin-react/tests/rolldown.test.ts | Adds unit test to verify the resolve plugin correctly handles base-prefixed refresh runtime IDs in bundledDev mode |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| ? [viteRefreshWrapper, viteConfigPost, viteReactRefreshBundledDevMode] | ||
| : []), | ||
| viteReactRefreshBundledDevModeResolveRuntime, | ||
| viteReactRefresh, |
There was a problem hiding this comment.
The resolve runtime plugin should be conditionally included only when isRolldownVite is true, similar to how other bundledDev-specific plugins are included on lines 555-557. While the plugin has internal guards that prevent it from running in non-rolldown contexts, it's more consistent and efficient to only include it when it can actually be used. Consider moving this line inside the conditional spread on line 556.
| ? [viteRefreshWrapper, viteConfigPost, viteReactRefreshBundledDevMode] | |
| : []), | |
| viteReactRefreshBundledDevModeResolveRuntime, | |
| viteReactRefresh, | |
| ? [ | |
| viteRefreshWrapper, | |
| viteConfigPost, | |
| viteReactRefreshBundledDevMode, | |
| viteReactRefreshBundledDevModeResolveRuntime, | |
| ] | |
| : []), | |
| viteReact, |
|
|
||
| const reactBabelPlugin = plugins.find( | ||
| (plugin) => plugin.name === 'vite:react-babel', | ||
| ) |
There was a problem hiding this comment.
The test should include an assertion to verify that reactBabelPlugin is found before attempting to call its configResolved method. While optional chaining prevents runtime errors, an explicit assertion would make the test more robust and provide clearer feedback if the plugin structure changes. Add expect(reactBabelPlugin).toBeDefined() after line 30.
| ) | |
| ) | |
| expect(reactBabelPlugin).toBeDefined() |
| const resolved = (reactRefreshResolvePlugin?.resolveId as any)( | ||
| '/ui' + runtimePublicPath, | ||
| ) |
There was a problem hiding this comment.
The test hardcodes '/ui' instead of deriving it from the base value set on line 32. For better maintainability, consider constructing the test input consistently: either use '/ui/'.slice(0, -1) + runtimePublicPath or store the base in a variable and reuse it. This ensures the test remains correct if the base value is changed.
Fixes vitejs/vite#21679.
This follows maintainer guidance from vitejs/vite#21704 to handle the issue on the plugin-react side.
What changes
Validation
Implementation is original and written fresh for this repo path.