Skip to content

Commit 4b4a95c

Browse files
authored
fix: don't use retainLines with react compiler (#319)
1 parent 2b7f2ae commit 4b4a95c

File tree

24 files changed

+478
-9
lines changed

24 files changed

+478
-9
lines changed

.npmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
hoist-pattern[]=@emotion/* # playground/react-emotion
2+
hoist-pattern[]=*babel*
23
strict-peer-dependencies=false
34
shell-emulator=true
45
auto-install-peers=false

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@
3131
"ci-publish": "tsx scripts/publishCI.ts"
3232
},
3333
"devDependencies": {
34-
"@eslint-types/typescript-eslint": "^7.5.0",
3534
"@eslint-types/import": "^2.29.1",
35+
"@eslint-types/typescript-eslint": "^7.5.0",
3636
"@types/fs-extra": "^11.0.4",
3737
"@types/node": "^20.12.12",
3838
"@typescript-eslint/eslint-plugin": "^7.9.0",

packages/plugin-react/CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,23 @@
22

33
## Unreleased
44

5+
### Fix support for React compiler
6+
7+
Don't set `retainLines: true` when the React compiler is used. This creates whitespace issues and the compiler is modifying the JSX too much to get correct line numbers after that. If you want to use the React compiler and get back correct line numbers for tools like [vite-plugin-react-click-to-component](https://github.com/ArnaudBarre/vite-plugin-react-click-to-component) to work, you should update your config to something like:
8+
9+
```ts
10+
export default defineConfig(({ command }) => {
11+
const babelPlugins = [['babel-plugin-react-compiler', {}]]
12+
if (command === 'serve') {
13+
babelPlugins.push(['@babel/plugin-transform-react-jsx-development', {}])
14+
}
15+
16+
return {
17+
plugins: [react({ babel: { plugins: babelPlugins } })],
18+
}
19+
})
20+
```
21+
522
### Support HMR for class components
623

724
This is a long overdue and should fix some issues people had with HMR when migrating from CRA.

packages/plugin-react/src/index.ts

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,11 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
235235
filename: id,
236236
sourceFileName: filepath,
237237
// Required for esbuild.jsxDev to provide correct line numbers
238-
retainLines: !isProduction && isJSX && opts.jsxRuntime !== 'classic',
238+
// This crates issues the react compiler because the re-order is too important
239+
// People should use @babel/plugin-transform-react-jsx-development to get back good line numbers
240+
retainLines: hasCompiler(plugins)
241+
? false
242+
: !isProduction && isJSX && opts.jsxRuntime !== 'classic',
239243
parserOpts: {
240244
...babelOptions.parserOpts,
241245
sourceType: 'module',
@@ -264,16 +268,23 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
264268
},
265269
}
266270

271+
// We can't add `react-dom` because the dependency is `react-dom/client`
272+
// for React 18 while it's `react-dom` for React 17. We'd need to detect
273+
// what React version the user has installed.
274+
const dependencies = ['react', jsxImportDevRuntime, jsxImportRuntime]
275+
const staticBabelPlugins =
276+
typeof opts.babel === 'object' ? opts.babel?.plugins ?? [] : []
277+
if (hasCompiler(staticBabelPlugins)) {
278+
dependencies.push('react/compiler-runtime')
279+
}
280+
267281
const viteReactRefresh: Plugin = {
268282
name: 'vite:react-refresh',
269283
enforce: 'pre',
270284
config: (userConfig) => ({
271285
build: silenceUseClientWarning(userConfig),
272286
optimizeDeps: {
273-
// We can't add `react-dom` because the dependency is `react-dom/client`
274-
// for React 18 while it's `react-dom` for React 17. We'd need to detect
275-
// what React version the user has installed.
276-
include: ['react', jsxImportDevRuntime, jsxImportRuntime],
287+
include: dependencies,
277288
},
278289
resolve: {
279290
dedupe: ['react', 'react-dom'],
@@ -357,3 +368,11 @@ function createBabelOptions(rawOptions?: BabelOptions) {
357368
function defined<T>(value: T | undefined): value is T {
358369
return value !== undefined
359370
}
371+
372+
function hasCompiler(plugins: ReactBabelOptions['plugins']) {
373+
return plugins.some(
374+
(p) =>
375+
p === 'babel-plugin-react-compiler' ||
376+
(Array.isArray(p) && p[0] === 'babel-plugin-react-compiler'),
377+
)
378+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { expect, test } from 'vitest'
2+
import { editFile, isServe, page, untilUpdated } from '~utils'
3+
4+
test('should render', async () => {
5+
expect(await page.textContent('button')).toMatch('count is 0')
6+
expect(await page.click('button'))
7+
expect(await page.textContent('button')).toMatch('count is 1')
8+
})
9+
10+
test.runIf(isServe)('should hmr', async () => {
11+
editFile('src/App.tsx', (code) =>
12+
code.replace('count is {count}', 'count is {count}!'),
13+
)
14+
await untilUpdated(() => page.textContent('button'), 'count is 1!')
15+
})

playground/compiler/index.html

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+
<title>Vite + React + TS</title>
8+
</head>
9+
<body>
10+
<div id="root"></div>
11+
<script type="module" src="/src/main.tsx"></script>
12+
</body>
13+
</html>

playground/compiler/package.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"name": "@vitejs/compiler",
3+
"private": true,
4+
"type": "module",
5+
"scripts": {
6+
"dev": "vite",
7+
"build": "tsc && vite build",
8+
"preview": "vite preview"
9+
},
10+
"dependencies": {
11+
"react": "^19.0.0-rc-3f1436cca1-20240516",
12+
"react-dom": "^19.0.0-rc-3f1436cca1-20240516"
13+
},
14+
"devDependencies": {
15+
"@babel/plugin-transform-react-jsx-development": "^7.22.5",
16+
"@types/react": "^18.3.2",
17+
"@types/react-dom": "^18.3.0",
18+
"@vitejs/plugin-react": "workspace:*",
19+
"babel-plugin-react-compiler": "^0.0.0-experimental-592953e-20240517",
20+
"typescript": "^5.4.5",
21+
"vite": "^5.2.11"
22+
}
23+
}

playground/compiler/public/vite.svg

Lines changed: 1 addition & 0 deletions
Loading

playground/compiler/src/App.css

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#root {
2+
max-width: 1280px;
3+
margin: 0 auto;
4+
padding: 2rem;
5+
text-align: center;
6+
}
7+
8+
.logo {
9+
height: 6em;
10+
padding: 1.5em;
11+
will-change: filter;
12+
transition: filter 300ms;
13+
}
14+
.logo:hover {
15+
filter: drop-shadow(0 0 2em #646cffaa);
16+
}
17+
.logo.react:hover {
18+
filter: drop-shadow(0 0 2em #61dafbaa);
19+
}
20+
21+
@keyframes logo-spin {
22+
from {
23+
transform: rotate(0deg);
24+
}
25+
to {
26+
transform: rotate(360deg);
27+
}
28+
}
29+
30+
@media (prefers-reduced-motion: no-preference) {
31+
a:nth-of-type(2) .logo {
32+
animation: logo-spin infinite 20s linear;
33+
}
34+
}
35+
36+
.card {
37+
padding: 2em;
38+
}
39+
40+
.read-the-docs {
41+
color: #888;
42+
}

playground/compiler/src/App.tsx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { useState } from 'react'
2+
import './App.css'
3+
4+
export function App() {
5+
const [count, setCount] = useState(0)
6+
7+
return (
8+
<>
9+
<h1>Vite + React</h1>
10+
<div className="card">
11+
<button onClick={() => setCount((count) => count + 1)}>
12+
count is {count}
13+
</button>
14+
<p>
15+
Edit <code>src/App.tsx</code> and save to test HMR
16+
</p>
17+
</div>
18+
<p className="read-the-docs">
19+
Click on the Vite and React logos to learn more
20+
</p>
21+
</>
22+
)
23+
}

0 commit comments

Comments
 (0)