Skip to content

Commit bdae7fa

Browse files
authored
fix(react): sourcemap incorrect warning and classic runtime sourcemap (#9006)
1 parent da7c3ae commit bdae7fa

9 files changed

Lines changed: 104 additions & 5 deletions

File tree

packages/plugin-react/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
"@babel/plugin-transform-react-jsx-development": "^7.18.6",
4545
"@babel/plugin-transform-react-jsx-self": "^7.18.6",
4646
"@babel/plugin-transform-react-jsx-source": "^7.18.6",
47+
"magic-string": "^0.26.2",
4748
"react-refresh": "^0.14.0"
4849
},
4950
"peerDependencies": {

packages/plugin-react/src/index.ts

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import type { ParserOptions, TransformOptions, types as t } from '@babel/core'
33
import * as babel from '@babel/core'
44
import { createFilter, normalizePath } from 'vite'
55
import type { Plugin, PluginOption, ResolvedConfig } from 'vite'
6+
import MagicString from 'magic-string'
7+
import type { SourceMap } from 'magic-string'
68
import {
79
addRefreshWrapper,
810
isRefreshBoundary,
@@ -88,11 +90,14 @@ declare module 'vite' {
8890
}
8991
}
9092

93+
const prependReactImportCode = "import React from 'react'; "
94+
9195
export default function viteReact(opts: Options = {}): PluginOption[] {
9296
// Provide default values for Rollup compat.
9397
let devBase = '/'
9498
let resolvedCacheDir: string
9599
let filter = createFilter(opts.include, opts.exclude)
100+
let needHiresSourcemap = false
96101
let isProduction = true
97102
let projectRoot = process.cwd()
98103
let skipFastRefresh = opts.fastRefresh === false
@@ -135,6 +140,8 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
135140
filter = createFilter(opts.include, opts.exclude, {
136141
resolve: projectRoot
137142
})
143+
needHiresSourcemap =
144+
config.command === 'build' && !!config.build.sourcemap
138145
isProduction = config.isProduction
139146
skipFastRefresh ||= isProduction || config.command === 'build'
140147

@@ -217,6 +224,7 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
217224
}
218225

219226
let ast: t.File | null | undefined
227+
let prependReactImport = false
220228
if (!isProjectFile || isJSX) {
221229
if (useAutomaticRuntime) {
222230
// By reverse-compiling "React.createElement" calls into JSX,
@@ -261,11 +269,23 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
261269
// Even if the automatic JSX runtime is not used, we can still
262270
// inject the React import for .jsx and .tsx modules.
263271
if (!skipReactImport && !importReactRE.test(code)) {
264-
code = `import React from 'react'; ` + code
272+
prependReactImport = true
265273
}
266274
}
267275
}
268276

277+
let inputMap: SourceMap | undefined
278+
if (prependReactImport) {
279+
if (needHiresSourcemap) {
280+
const s = new MagicString(code)
281+
s.prepend(prependReactImportCode)
282+
code = s.toString()
283+
inputMap = s.generateMap({ hires: true, source: id })
284+
} else {
285+
code = prependReactImportCode + code
286+
}
287+
}
288+
269289
// Plugins defined through this Vite plugin are only applied
270290
// to modules within the project root, but "babel.config.js"
271291
// files can define plugins that need to be applied to every
@@ -275,10 +295,11 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
275295
!babelOptions.configFile &&
276296
!(isProjectFile && babelOptions.babelrc)
277297

298+
// Avoid parsing if no plugins exist.
278299
if (shouldSkip) {
279-
// Avoid parsing if no plugins exist.
280300
return {
281-
code
301+
code,
302+
map: inputMap ?? null
282303
}
283304
}
284305

@@ -326,7 +347,7 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
326347
plugins,
327348
sourceMaps: true,
328349
// Vite handles sourcemap flattening
329-
inputSourceMap: false as any
350+
inputSourceMap: inputMap ?? (false as any)
330351
})
331352

332353
if (result) {

playground/react-sourcemap/App.jsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
console.log('App.jsx 1') // for sourcemap
2+
function App() {
3+
return <div>foo</div>
4+
}
5+
6+
console.log('App.jsx 2') // for sourcemap
7+
8+
export default App
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { isBuild, serverLogs } from '~utils'
2+
3+
test.runIf(isBuild)('should not output sourcemap warning', () => {
4+
serverLogs.forEach((log) => {
5+
expect(log).not.toMatch('Sourcemap is likely to be incorrect')
6+
})
7+
})
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<div id="app"></div>
2+
<script type="module" src="main.jsx"></script>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import React from 'react'
2+
import ReactDOM from 'react-dom/client'
3+
import App from './App.jsx'
4+
5+
ReactDOM.createRoot(document.getElementById('app')).render(
6+
React.createElement(App)
7+
)
8+
9+
console.log('main.jsx') // for sourcemap
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"name": "test-react-sourcemap",
3+
"private": true,
4+
"version": "0.0.0",
5+
"scripts": {
6+
"dev": "vite",
7+
"dev:classic": "cross-env USE_CLASSIC=1 vite",
8+
"build": "vite build",
9+
"build:classic": "cross-env USE_CLASSIC=1 vite build",
10+
"debug": "node --inspect-brk ../../packages/vite/bin/vite",
11+
"preview": "vite preview"
12+
},
13+
"dependencies": {
14+
"react": "^18.2.0",
15+
"react-dom": "^18.2.0"
16+
},
17+
"devDependencies": {
18+
"@vitejs/plugin-react": "workspace:*",
19+
"cross-env": "^7.0.3"
20+
}
21+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import react from '@vitejs/plugin-react'
2+
import type { UserConfig } from 'vite'
3+
4+
const config: UserConfig = {
5+
plugins: [
6+
react({
7+
jsxRuntime: process.env.USE_CLASSIC === '1' ? 'classic' : 'automatic'
8+
})
9+
],
10+
build: {
11+
sourcemap: true
12+
}
13+
}
14+
15+
export default config

pnpm-lock.yaml

Lines changed: 16 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)