Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/plugin-react/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### Add `compiler.logDiagnostics` option

Recoverable React Compiler diagnostics are no longer logged by default. Set `compiler.logDiagnostics` to `true` to log them through Vite. Fatal diagnostics are always logged and fail the transform.

## 6.1.0 (2026-08-19)

### Add experimental native React Compiler support ([#1419](https://github.com/vitejs/vite-plugin-react/pull/1419))
Expand Down
6 changes: 6 additions & 0 deletions packages/plugin-react/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,12 @@ The `compiler` option also accepts [React Compiler options](https://react.dev/re
react({ compiler: { compilationMode: 'annotation' } })
```

Set `logDiagnostics` to `true` to log recoverable React Compiler diagnostics through Vite. This option defaults to `false`. Fatal diagnostics are always logged and fail the transform.

```js
react({ compiler: { logDiagnostics: true } })
```

### Babel React Compiler

React Compiler can also be used through Babel with the exported `reactCompilerPreset` helper. This requires [`@rolldown/plugin-babel`](https://npmx.dev/package/@rolldown/plugin-babel), [`babel-plugin-react-compiler`](https://npmx.dev/package/babel-plugin-react-compiler), and [`@babel/core`](https://npmx.dev/package/@babel/core) as peer dependencies:
Expand Down
27 changes: 19 additions & 8 deletions packages/plugin-react/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@ import { defaultCodeFilter, reactCompilerPreset } from './reactCompilerPreset'
const _dirname = dirname(fileURLToPath(import.meta.url))
const refreshRuntimePath = join(_dirname, 'refresh-runtime.js')

interface ReactCompilerPluginOptions extends ReactCompilerOptions {
/**
* Log recoverable React Compiler diagnostics through Vite.
* Fatal diagnostics are always logged and fail the transform.
* @default false
*/
logDiagnostics?: boolean
}

export interface Options {
/**
* Can be used to process extra files like `.mdx`
Expand Down Expand Up @@ -59,7 +68,7 @@ export interface Options {
* @default false
* @experimental
*/
compiler?: boolean | ReactCompilerOptions
compiler?: boolean | ReactCompilerPluginOptions
}

const defaultIncludeRE = /\.[tj]sx?$/
Expand Down Expand Up @@ -288,7 +297,7 @@ export default function viteReact(opts: Options = {}): Plugin[] {
}

function createReactCompilerPlugin(
options: ReactCompilerOptions,
{ logDiagnostics, ...reactCompilerOptions }: ReactCompilerPluginOptions,
include: NonNullable<Options['include']>,
exclude: NonNullable<Options['exclude']>,
reactOptions: Pick<Options, 'jsxRuntime' | 'jsxImportSource'>,
Expand All @@ -298,7 +307,7 @@ function createReactCompilerPlugin(
let jsxDevelopment = false
let compiler: typeof import('oxc-transform-react') | undefined
const runtime =
options.target === '17' || options.target === '18'
reactCompilerOptions.target === '17' || reactCompilerOptions.target === '18'
? 'react-compiler-runtime'
: 'react/compiler-runtime'

Expand Down Expand Up @@ -344,7 +353,7 @@ function createReactCompilerPlugin(
const isClient = this.environment?.config.consumer !== 'server'
const shouldCompile =
isClient &&
(options.compilationMode === 'annotation'
(reactCompilerOptions.compilationMode === 'annotation'
? /['"]use memo['"]/.test(code)
: defaultCodeFilter.test(code))
// The config hook is not called when the plugin is used with Rolldown directly.
Expand All @@ -358,7 +367,7 @@ function createReactCompilerPlugin(
importSource: reactOptions.jsxImportSource,
refresh: isClient && isFastRefreshEnabled(),
},
reactCompiler: shouldCompile ? options : false,
reactCompiler: shouldCompile ? reactCompilerOptions : false,
sourcemap,
})
const diagnostics = result.errors.map(
Expand All @@ -371,8 +380,10 @@ function createReactCompilerPlugin(
diagnostics.join('\n\n') || 'React Compiler transform failed.',
)
}
for (const diagnostic of diagnostics) {
this.warn(diagnostic)
if (logDiagnostics) {
for (const diagnostic of diagnostics) {
this.warn(diagnostic)
}
}

return { code: result.code, map: result.map }
Expand All @@ -384,7 +395,7 @@ function createReactCompilerPlugin(
viteReact.preambleCode = preambleCode

export { reactCompilerPreset }
export type { ReactCompilerOptions }
export type { ReactCompilerPluginOptions as ReactCompilerOptions }

// Compat for require
function viteReactForCjs(this: unknown, options: Options): Plugin[] {
Expand Down
36 changes: 30 additions & 6 deletions packages/plugin-react/tests/reactCompiler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,12 +101,38 @@ describe('compiler option', () => {
expect(withoutSourcemap.map).toBeFalsy()
expect((await transformWithBuildConfig({}, true)).map).toBeTruthy()
})

test('logs recoverable diagnostics when enabled', async () => {
const diagnostics: unknown[] = []

await transformWithBuildConfig(
{ logDiagnostics: true },
false,
'client',
`
import { useState } from 'react'

export function App({ condition }) {
if (condition) useState(0)
return <div />
}
`,
(diagnostic) => diagnostics.push(diagnostic),
)

expect(diagnostics).toHaveLength(1)
expect(diagnostics[0]).toContain(
'Hooks must always be called in a consistent order',
)
})
})

async function transformWithBuildConfig(
compiler: ReactCompilerOptions,
buildSourcemap: boolean,
consumer: 'client' | 'server' = 'client',
code: string = `export function App({ name }) { return <div>{name}</div> }`,
onWarn?: (message: unknown) => void,
) {
const plugin = pluginReact({ compiler }).find(
(plugin) => plugin.name === 'vite:react-compiler',
Expand All @@ -115,7 +141,9 @@ async function transformWithBuildConfig(
error(message: unknown): never {
throw new Error(String(message))
},
warn() {},
warn(message: unknown) {
onWarn?.(message)
},
environment: { config: { consumer } },
}

Expand All @@ -142,11 +170,7 @@ async function transformWithBuildConfig(
if (typeof plugin.transform !== 'object') {
throw new Error('Missing transform hook')
}
return plugin.transform.handler.call(
context as any,
`export function App({ name }) { return <div>{name}</div> }`,
'/entry.tsx',
)
return plugin.transform.handler.call(context as any, code, '/entry.tsx')
}

async function getViteReactConfig(
Expand Down
Loading