diff --git a/packages/plugin-react/src/index.ts b/packages/plugin-react/src/index.ts index 3dba7be8f..a147b5e17 100644 --- a/packages/plugin-react/src/index.ts +++ b/packages/plugin-react/src/index.ts @@ -172,7 +172,10 @@ export default function viteReact(opts: Options = {}): Plugin[] { { tag: 'script', attrs: { type: 'module' }, - children: getPreambleCode(base), + // In bundled dev mode, Rolldown resolves module specifiers at build + // time without URL-level base stripping, so we must use '/' instead + // of config.base to match the resolveId hook for '/@react-refresh'. + children: getPreambleCode('/'), }, ] }, diff --git a/playground/bundled-dev-base-path/App.jsx b/playground/bundled-dev-base-path/App.jsx new file mode 100644 index 000000000..996aa4af2 --- /dev/null +++ b/playground/bundled-dev-base-path/App.jsx @@ -0,0 +1,15 @@ +import { useState } from 'react' + +function App() { + const [count, setCount] = useState(0) + return ( +
+

bundledDev + base path

+ +
+ ) +} + +export default App diff --git a/playground/bundled-dev-base-path/__tests__/bundled-dev-base-path.spec.ts b/playground/bundled-dev-base-path/__tests__/bundled-dev-base-path.spec.ts new file mode 100644 index 000000000..5b04223f3 --- /dev/null +++ b/playground/bundled-dev-base-path/__tests__/bundled-dev-base-path.spec.ts @@ -0,0 +1,29 @@ +import { expect, test } from 'vitest' +import { browserErrors, isServe, page, viteTestUrl } from '~utils' + +// Regression test for https://github.com/vitejs/vite-plugin-react/issues/1190 +// bundledDev mode with a non-root base should not fail to resolve /@react-refresh. +test.runIf(isServe)( + 'should load without UNRESOLVED_IMPORT error for @react-refresh', + async () => { + await page.goto(viteTestUrl) + // bundledDev shows "Bundling in progress" until the first bundle is ready. + await page.waitForSelector('h1') + await expect + .poll(() => page.textContent('h1')) + .toMatch('bundledDev + base path') + expect(browserErrors).toHaveLength(0) + }, +) + +test.runIf(isServe)('should render and update state', async () => { + await page.goto(viteTestUrl) + await page.waitForSelector('#state-button') + await expect + .poll(() => page.textContent('#state-button')) + .toMatch('count is: 0') + await page.click('#state-button') + await expect + .poll(() => page.textContent('#state-button')) + .toMatch('count is: 1') +}) diff --git a/playground/bundled-dev-base-path/index.html b/playground/bundled-dev-base-path/index.html new file mode 100644 index 000000000..7417c442d --- /dev/null +++ b/playground/bundled-dev-base-path/index.html @@ -0,0 +1,10 @@ +
+ diff --git a/playground/bundled-dev-base-path/package.json b/playground/bundled-dev-base-path/package.json new file mode 100644 index 000000000..873ffc10e --- /dev/null +++ b/playground/bundled-dev-base-path/package.json @@ -0,0 +1,17 @@ +{ + "name": "@vitejs/test-bundled-dev-base-path", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "react": "^19.2.5", + "react-dom": "^19.2.5" + }, + "devDependencies": { + "@vitejs/plugin-react": "workspace:*" + } +} diff --git a/playground/bundled-dev-base-path/vite.config.ts b/playground/bundled-dev-base-path/vite.config.ts new file mode 100644 index 000000000..23ca63dd7 --- /dev/null +++ b/playground/bundled-dev-base-path/vite.config.ts @@ -0,0 +1,13 @@ +import react from '@vitejs/plugin-react' +import type { UserConfig } from 'vite' + +const config: UserConfig = { + server: { port: 8930 /* Should be unique */ }, + mode: 'development', + base: '/static/', + plugins: [react()], + experimental: { bundledDev: true }, + build: { minify: false }, +} + +export default config diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index eb61aaccd..7aec66268 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -793,6 +793,19 @@ importers: specifier: ^3.3.2 version: 3.3.2 + playground/bundled-dev-base-path: + dependencies: + react: + specifier: ^19.2.5 + version: 19.2.5 + react-dom: + specifier: ^19.2.5 + version: 19.2.5(react@19.2.5) + devDependencies: + '@vitejs/plugin-react': + specifier: workspace:* + version: link:../../packages/plugin-react + playground/class-components: dependencies: react: