Skip to content

fix(plugin-react): resolve base-prefixed refresh runtime in bundledDev#1121

Open
Vishnuuuu24 wants to merge 1 commit intovitejs:mainfrom
Vishnuuuu24:fix/plugin-react-bundleddev-base-refresh-21679
Open

fix(plugin-react): resolve base-prefixed refresh runtime in bundledDev#1121
Vishnuuuu24 wants to merge 1 commit intovitejs:mainfrom
Vishnuuuu24:fix/plugin-react-bundleddev-base-refresh-21679

Conversation

@Vishnuuuu24
Copy link

@Vishnuuuu24 Vishnuuuu24 commented Feb 25, 2026

Fixes vitejs/vite#21679.

This follows maintainer guidance from vitejs/vite#21704 to handle the issue on the plugin-react side.

What changes

  • add a pre resolve hook in plugin-react that normalizes base-prefixed refresh runtime ids in bundledDev mode
  • map base-prefixed /@react-refresh back to /@react-refresh when Fast Refresh is enabled
  • add a regression unit test for /ui/@react-refresh -> /@react-refresh in bundledDev

Validation

  • pnpm --filter @vitejs/plugin-react run test-unit -- tests/rolldown.test.ts
  • pnpm --filter @vitejs/plugin-react run test-unit
  • pnpm --filter @vitejs/plugin-react exec tsc -p tsconfig.json --noEmit

Implementation is original and written fresh for this repo path.

Copilot AI review requested due to automatic review settings February 25, 2026 06:35
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines 556 to 559
? [viteRefreshWrapper, viteConfigPost, viteReactRefreshBundledDevMode]
: []),
viteReactRefreshBundledDevModeResolveRuntime,
viteReactRefresh,
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
? [viteRefreshWrapper, viteConfigPost, viteReactRefreshBundledDevMode]
: []),
viteReactRefreshBundledDevModeResolveRuntime,
viteReactRefresh,
? [
viteRefreshWrapper,
viteConfigPost,
viteReactRefreshBundledDevMode,
viteReactRefreshBundledDevModeResolveRuntime,
]
: []),
viteReact,

Copilot uses AI. Check for mistakes.

const reactBabelPlugin = plugins.find(
(plugin) => plugin.name === 'vite:react-babel',
)
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
)
)
expect(reactBabelPlugin).toBeDefined()

Copilot uses AI. Check for mistakes.
Comment on lines +46 to +48
const resolved = (reactRefreshResolvePlugin?.resolveId as any)(
'/ui' + runtimePublicPath,
)
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

could not resolve react refresh when using bundledDev and base

2 participants