diff --git a/packages/plugin-react/src/index.ts b/packages/plugin-react/src/index.ts
index 3e7ef4e9c4e384..9aa3f94dc5b2f7 100644
--- a/packages/plugin-react/src/index.ts
+++ b/packages/plugin-react/src/index.ts
@@ -276,7 +276,10 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
!(isProjectFile && babelOptions.babelrc)
if (shouldSkip) {
- return // Avoid parsing if no plugins exist.
+ // Avoid parsing if no plugins exist.
+ return {
+ code
+ }
}
const parserPlugins: typeof babelOptions.parserOpts.plugins = [
diff --git a/playground/react-classic/App.jsx b/playground/react-classic/App.jsx
new file mode 100644
index 00000000000000..1de7461b163776
--- /dev/null
+++ b/playground/react-classic/App.jsx
@@ -0,0 +1,30 @@
+import { useState } from 'react'
+
+function App() {
+ const [count, setCount] = useState(0)
+ return (
+
+
+ Hello Vite + React
+
+
+
+
+ Edit App.jsx
and save to test HMR updates.
+
+
+ Learn React
+
+
+
+ )
+}
+
+export default App
diff --git a/playground/react-classic/__tests__/react.spec.ts b/playground/react-classic/__tests__/react.spec.ts
new file mode 100644
index 00000000000000..8381992d59df3d
--- /dev/null
+++ b/playground/react-classic/__tests__/react.spec.ts
@@ -0,0 +1,38 @@
+import { editFile, isServe, page, untilUpdated } from '~utils'
+
+test('should render', async () => {
+ expect(await page.textContent('h1')).toMatch('Hello Vite + React')
+})
+
+test('should update', async () => {
+ expect(await page.textContent('button')).toMatch('count is: 0')
+ await page.click('button')
+ expect(await page.textContent('button')).toMatch('count is: 1')
+})
+
+test('should hmr', async () => {
+ editFile('App.jsx', (code) => code.replace('Vite + React', 'Updated'))
+ await untilUpdated(() => page.textContent('h1'), 'Hello Updated')
+ // preserve state
+ expect(await page.textContent('button')).toMatch('count is: 1')
+})
+
+test.runIf(isServe)(
+ 'should have annotated jsx with file location metadata',
+ async () => {
+ const meta = await page.evaluate(() => {
+ const button = document.querySelector('button')
+ const key = Object.keys(button).find(
+ (key) => key.indexOf('__reactFiber') === 0
+ )
+ return button[key]._debugSource
+ })
+ // If the evaluate call doesn't crash, and the returned metadata has
+ // the expected fields, we're good.
+ expect(Object.keys(meta).sort()).toEqual([
+ 'columnNumber',
+ 'fileName',
+ 'lineNumber'
+ ])
+ }
+)
diff --git a/playground/react-classic/index.html b/playground/react-classic/index.html
new file mode 100644
index 00000000000000..f0015ceb9829a3
--- /dev/null
+++ b/playground/react-classic/index.html
@@ -0,0 +1,10 @@
+
+
diff --git a/playground/react-classic/package.json b/playground/react-classic/package.json
new file mode 100644
index 00000000000000..a07684af1feefd
--- /dev/null
+++ b/playground/react-classic/package.json
@@ -0,0 +1,23 @@
+{
+ "name": "test-react-classic",
+ "private": true,
+ "version": "0.0.0",
+ "scripts": {
+ "dev": "vite",
+ "build": "vite build",
+ "debug": "node --inspect-brk ../../packages/vite/bin/vite",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "react": "^18.1.0",
+ "react-dom": "^18.1.0"
+ },
+ "devDependencies": {
+ "@vitejs/plugin-react": "workspace:*"
+ },
+ "babel": {
+ "presets": [
+ "@babel/preset-env"
+ ]
+ }
+}
diff --git a/playground/react-classic/vite.config.ts b/playground/react-classic/vite.config.ts
new file mode 100644
index 00000000000000..a2044e99ae2f3c
--- /dev/null
+++ b/playground/react-classic/vite.config.ts
@@ -0,0 +1,16 @@
+import react from '@vitejs/plugin-react'
+import type { UserConfig } from 'vite'
+
+const config: UserConfig = {
+ plugins: [
+ react({
+ jsxRuntime: 'classic'
+ })
+ ],
+ build: {
+ // to make tests faster
+ minify: false
+ }
+}
+
+export default config
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 55ad67574c0778..6a036ebe08ce13 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -759,6 +759,17 @@ importers:
devDependencies:
'@vitejs/plugin-react': link:../../packages/plugin-react
+ playground/react-classic:
+ specifiers:
+ '@vitejs/plugin-react': workspace:*
+ react: ^18.1.0
+ react-dom: ^18.1.0
+ dependencies:
+ react: 18.2.0
+ react-dom: 18.2.0_react@18.2.0
+ devDependencies:
+ '@vitejs/plugin-react': link:../../packages/plugin-react
+
playground/react-emotion:
specifiers:
'@babel/plugin-proposal-pipeline-operator': ^7.18.2