From a7773347b690a8444165d1a8c304ac6b8095f28c Mon Sep 17 00:00:00 2001 From: Vishnu Vardhan P Date: Wed, 25 Feb 2026 12:03:53 +0530 Subject: [PATCH] fix(plugin-react): normalize base-prefixed refresh runtime in bundledDev --- packages/plugin-react/src/index.ts | 15 ++++++++++ packages/plugin-react/tests/rolldown.test.ts | 29 ++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/packages/plugin-react/src/index.ts b/packages/plugin-react/src/index.ts index 1664117dc..10c2a71ec 100644 --- a/packages/plugin-react/src/index.ts +++ b/packages/plugin-react/src/index.ts @@ -481,6 +481,20 @@ export default function viteReact(opts: Options = {}): Plugin[] { }, } + const viteReactRefreshBundledDevModeResolveRuntime: Plugin = { + name: 'vite:react-refresh-fbm-resolve-runtime', + enforce: 'pre', + resolveId(id) { + if (skipFastRefresh || !isBundledDev || base === '/') return + + const basePrefixedRuntimePublicPath = + base.slice(0, -1) + runtimePublicPath + if (id === basePrefixedRuntimePublicPath) { + return runtimePublicPath + } + }, + } + const dependencies = [ 'react', 'react-dom', @@ -541,6 +555,7 @@ export default function viteReact(opts: Options = {}): Plugin[] { ...(isRolldownVite ? [viteRefreshWrapper, viteConfigPost, viteReactRefreshBundledDevMode] : []), + viteReactRefreshBundledDevModeResolveRuntime, viteReactRefresh, virtualPreamblePlugin({ name: '@vitejs/plugin-react/preamble', diff --git a/packages/plugin-react/tests/rolldown.test.ts b/packages/plugin-react/tests/rolldown.test.ts index ab9c0b16d..92563f84e 100644 --- a/packages/plugin-react/tests/rolldown.test.ts +++ b/packages/plugin-react/tests/rolldown.test.ts @@ -1,4 +1,5 @@ import path from 'node:path' +import { runtimePublicPath } from '@vitejs/react-common' import { type Plugin, rolldown } from 'rolldown' import { expect, test } from 'vitest' import pluginReact, { type Options } from '../src/index.ts' @@ -21,6 +22,34 @@ test('HMR related code should not be included when using rolldown with babel plu expect(output[0].code).not.toContain('import.meta.hot') }) +test('resolves base-prefixed refresh runtime id in bundledDev mode', () => { + const plugins = pluginReact() + + const reactBabelPlugin = plugins.find( + (plugin) => plugin.name === 'vite:react-babel', + ) + reactBabelPlugin?.configResolved?.({ + base: '/ui/', + command: 'serve', + isProduction: false, + root: '/', + server: { hmr: true }, + plugins: [], + experimental: { bundledDev: true }, + } as any) + + const reactRefreshResolvePlugin = plugins.find( + (plugin) => plugin.name === 'vite:react-refresh-fbm-resolve-runtime', + ) + expect(reactRefreshResolvePlugin?.resolveId).toBeDefined() + + const resolved = (reactRefreshResolvePlugin?.resolveId as any)( + '/ui' + runtimePublicPath, + ) + + expect(resolved).toBe(runtimePublicPath) +}) + async function bundleWithRolldown(pluginReactOptions: Options = {}) { const ENTRY = '/entry.tsx' const files: Record = {