@vitejs/plugin-react 6.1.0 → 6.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -106,6 +106,12 @@ The `compiler` option also accepts [React Compiler options](https://react.dev/re
106
106
  react({ compiler: { compilationMode: 'annotation' } })
107
107
  ```
108
108
 
109
+ 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.
110
+
111
+ ```js
112
+ react({ compiler: { logDiagnostics: true } })
113
+ ```
114
+
109
115
  ### Babel React Compiler
110
116
 
111
117
  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:
package/dist/index.d.ts CHANGED
@@ -1,10 +1,19 @@
1
1
  import { Plugin } from "vite";
2
2
  import { ReactCompilerBabelPluginOptions, ReactCompilerOptions, RolldownBabelPreset } from "#optionalTypes";
3
3
  //#region src/reactCompilerPreset.d.ts
4
- declare const reactCompilerPreset: (options?: ReactCompilerBabelPluginOptions) => RolldownBabelPreset;
4
+ export declare const reactCompilerPreset: (options?: ReactCompilerBabelPluginOptions) => RolldownBabelPreset;
5
5
  //#endregion
6
6
  //#region src/index.d.ts
7
- interface Options {
7
+ interface ReactCompilerPluginOptions extends ReactCompilerOptions {
8
+ /**
9
+ * Log recoverable React Compiler diagnostics through Vite.
10
+ * Fatal diagnostics are always logged and fail the transform.
11
+ * @default false
12
+ * @deprecated Use `reportDiagnostics` instead
13
+ */
14
+ logDiagnostics?: boolean;
15
+ }
16
+ export interface Options {
8
17
  /**
9
18
  * Can be used to process extra files like `.mdx`
10
19
  * @example include: /\.(mdx|js|jsx|ts|tsx)$/
@@ -43,7 +52,7 @@ interface Options {
43
52
  * @default false
44
53
  * @experimental
45
54
  */
46
- compiler?: boolean | ReactCompilerOptions;
55
+ compiler?: boolean | ReactCompilerPluginOptions;
47
56
  }
48
57
  declare function viteReact(opts?: Options): Plugin[];
49
58
  declare namespace viteReact {
@@ -51,4 +60,4 @@ declare namespace viteReact {
51
60
  }
52
61
  declare function viteReactForCjs(this: unknown, options: Options): Plugin[];
53
62
  //#endregion
54
- export { Options, type ReactCompilerOptions, viteReact as default, viteReactForCjs as "module.exports", reactCompilerPreset };
63
+ export { type ReactCompilerPluginOptions as ReactCompilerOptions, viteReact as default, viteReactForCjs as "module.exports" };
package/dist/index.js CHANGED
@@ -194,11 +194,13 @@ function viteReact(opts = {}) {
194
194
  if (opts.compiler) plugins.unshift(createReactCompilerPlugin(opts.compiler === true ? {} : opts.compiler, include, exclude, opts, () => !skipFastRefresh));
195
195
  return plugins;
196
196
  }
197
- function createReactCompilerPlugin(options, include, exclude, reactOptions, isFastRefreshEnabled) {
198
- let sourcemap = true;
197
+ function createReactCompilerPlugin({ logDiagnostics, ...reactCompilerOptions }, include, exclude, reactOptions, isFastRefreshEnabled) {
199
198
  let jsxDevelopment = false;
200
199
  let compiler;
201
- const runtime = options.target === "17" || options.target === "18" ? "react-compiler-runtime" : "react/compiler-runtime";
200
+ const runtime = reactCompilerOptions.target === "17" || reactCompilerOptions.target === "18" ? "react-compiler-runtime" : "react/compiler-runtime";
201
+ const jsxImportSource = reactOptions.jsxImportSource ?? "react";
202
+ const jsxImportRuntime = `${jsxImportSource}/jsx-runtime`;
203
+ const jsxImportDevRuntime = `${jsxImportSource}/jsx-dev-runtime`;
202
204
  const loadCompiler = async (onError) => {
203
205
  if (compiler) return compiler;
204
206
  try {
@@ -215,7 +217,6 @@ function createReactCompilerPlugin(options, include, exclude, reactOptions, isFa
215
217
  return { optimizeDeps: { include: [runtime] } };
216
218
  },
217
219
  configResolved(config) {
218
- sourcemap = config.command !== "build" || !!config.build.sourcemap;
219
220
  jsxDevelopment = !config.isProduction;
220
221
  },
221
222
  transform: {
@@ -224,18 +225,24 @@ function createReactCompilerPlugin(options, include, exclude, reactOptions, isFa
224
225
  exclude: makeIdFiltersToMatchWithQuery(exclude)
225
226
  } },
226
227
  async handler(code, id) {
228
+ const [filepath] = id.split("?");
229
+ const isJSX = filepath.endsWith("x");
227
230
  const isClient = this.environment?.config.consumer !== "server";
228
- const shouldCompile = isClient && (options.compilationMode === "annotation" ? /['"]use memo['"]/.test(code) : defaultCodeFilter.test(code));
231
+ const refreshEnabled = isClient && isFastRefreshEnabled() && (isJSX || code.includes(jsxImportRuntime) || code.includes(jsxImportDevRuntime));
232
+ const shouldCompile = isClient && (reactCompilerOptions.compilationMode === "annotation" ? /['"]use memo['"]/.test(code) : defaultCodeFilter.test(code));
229
233
  const { transform } = compiler ?? await loadCompiler((message) => this.error(message));
230
234
  const result = await transform(id.split("?")[0], code, {
231
235
  jsx: {
232
236
  runtime: reactOptions.jsxRuntime,
233
237
  development: jsxDevelopment,
234
238
  importSource: reactOptions.jsxImportSource,
235
- refresh: isClient && isFastRefreshEnabled()
239
+ refresh: refreshEnabled
236
240
  },
237
- reactCompiler: shouldCompile ? options : false,
238
- sourcemap
241
+ reactCompiler: !shouldCompile ? false : logDiagnostics ? {
242
+ ...reactCompilerOptions,
243
+ reportDiagnostics: true
244
+ } : reactCompilerOptions,
245
+ sourcemap: this.environment ? this.environment.config.command !== "build" || !!this.environment.config.build.sourcemap : true
239
246
  });
240
247
  const diagnostics = result.errors.map((error) => `${error.message}${error.codeframe ? `\n${error.codeframe}` : ""}`);
241
248
  if (result.fatal) this.error(diagnostics.join("\n\n") || "React Compiler transform failed.");
@@ -112,6 +112,29 @@ function canPreserveStateBetween(prevType, nextType) {
112
112
  if (isReactClass(prevType) || isReactClass(nextType)) {
113
113
  return false
114
114
  }
115
+ // A fiber's tag is derived from the kind of its type (a plain function
116
+ // vs memo vs forwardRef), and the reconciler can only swap implementations
117
+ // in place within the same tag. If the kind changed, the tree must remount.
118
+ if (typeof prevType !== typeof nextType) {
119
+ return false
120
+ }
121
+ if (typeof prevType === 'object' && prevType !== null && nextType !== null) {
122
+ if (
123
+ getProperty(prevType, '$$typeof') !== getProperty(nextType, '$$typeof')
124
+ ) {
125
+ return false
126
+ }
127
+ // Switching from SimpleMemoComponent to MemoComponent requires a remount;
128
+ // for symmetry, remount for the reverse too.
129
+ if (getProperty(prevType, '$$typeof') === REACT_MEMO_TYPE) {
130
+ if (
131
+ (getProperty(prevType, 'compare') === null) !==
132
+ (getProperty(nextType, 'compare') === null)
133
+ ) {
134
+ return false
135
+ }
136
+ }
137
+ }
115
138
  if (haveEqualSignatures(prevType, nextType)) {
116
139
  return true
117
140
  }
@@ -609,50 +632,49 @@ export function validateRefreshBoundaryAndEnqueueUpdate(
609
632
  prevExports,
610
633
  nextExports,
611
634
  ) {
635
+ let hasExports = false
612
636
  const ignoredExports = window.__getReactRefreshIgnoredExports?.({ id }) ?? []
613
- if (
614
- predicateOnExport(
615
- ignoredExports,
616
- prevExports,
617
- (key) => key in nextExports,
618
- ) !== true
619
- ) {
620
- return 'Could not Fast Refresh (export removed)'
621
- }
622
- if (
623
- predicateOnExport(
624
- ignoredExports,
625
- nextExports,
626
- (key) => key in prevExports,
627
- ) !== true
628
- ) {
629
- return 'Could not Fast Refresh (new export)'
630
- }
631
637
 
632
- let hasExports = false
633
- const allExportsAreComponentsOrUnchanged = predicateOnExport(
634
- ignoredExports,
635
- nextExports,
636
- (key, value) => {
637
- hasExports = true
638
- if (isLikelyComponentType(value)) return true
639
- if (isCompoundComponent(value)) return true
640
- return prevExports[key] === nextExports[key]
641
- },
642
- )
643
- if (hasExports && allExportsAreComponentsOrUnchanged === true) {
644
- enqueueUpdate()
645
- } else {
646
- return `Could not Fast Refresh ("${allExportsAreComponentsOrUnchanged}" export is incompatible). Learn more at __README_URL__#consistent-components-exports`
638
+ for (const key in prevExports) {
639
+ if (ignoredExports.includes(key)) continue
640
+ if (!(key in nextExports)) {
641
+ return `Could not Fast Refresh (export "${key}" removed)`
642
+ }
647
643
  }
648
- }
649
644
 
650
- function predicateOnExport(ignoredExports, moduleExports, predicate) {
651
- for (const key in moduleExports) {
645
+ for (const key in nextExports) {
652
646
  if (ignoredExports.includes(key)) continue
653
- if (!predicate(key, moduleExports[key])) return key
647
+ hasExports = true
648
+ if (!(key in prevExports)) {
649
+ return `Could not Fast Refresh (export "${key}" added)`
650
+ }
651
+ if (isLikelyComponentType(nextExports[key])) continue
652
+ if (isCompoundComponent(nextExports[key])) {
653
+ if (!isCompoundComponent(prevExports[key])) {
654
+ return `Could not Fast Refresh (compound component "${key}" added)`
655
+ }
656
+ for (const subKey in prevExports[key]) {
657
+ if (!(subKey in nextExports[key])) {
658
+ return `Could not Fast Refresh (compound component "${key}.${subKey}" removed)`
659
+ }
660
+ }
661
+ for (const subKey in nextExports[key]) {
662
+ if (!(subKey in prevExports[key])) {
663
+ return `Could not Fast Refresh (compound component "${key}.${subKey}" added)`
664
+ }
665
+ }
666
+ continue
667
+ }
668
+ if (prevExports[key] !== nextExports[key]) {
669
+ return `Could not Fast Refresh ("${key}" export is incompatible). Learn more at __README_URL__#consistent-components-exports`
670
+ }
671
+ }
672
+
673
+ if (hasExports) {
674
+ enqueueUpdate()
675
+ } else {
676
+ return `No exports`
654
677
  }
655
- return true
656
678
  }
657
679
 
658
680
  // Hides vite-ignored dynamic import so that Vite can skip analysis if no other
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vitejs/plugin-react",
3
- "version": "6.1.0",
3
+ "version": "6.1.2",
4
4
  "description": "The default Vite plugin for React projects",
5
5
  "keywords": [
6
6
  "fast refresh",
@@ -46,21 +46,20 @@
46
46
  "@rolldown/pluginutils": "^1.0.1"
47
47
  },
48
48
  "devDependencies": {
49
- "@babel/core": "8.0.1",
50
- "@rolldown/plugin-babel": "^0.2.3",
49
+ "@rolldown/plugin-babel": "^0.2.4",
51
50
  "@vitejs/react-common": "workspace:*",
52
51
  "babel-plugin-react-compiler": "^1.0.0",
53
- "oxc-transform-react": "^0.145.0",
54
- "react": "^19.2.8",
55
- "react-dom": "^19.2.8",
56
- "rolldown": "^1.2.3",
57
- "tsdown": "^0.22.14",
58
- "vite": "^8.2.1"
52
+ "oxc-transform-react": "^0.152.0",
53
+ "react": "^19.3.0",
54
+ "react-dom": "^19.3.0",
55
+ "rolldown": "^1.2.12",
56
+ "tsdown": "^0.23.0",
57
+ "vite": "^8.3.2"
59
58
  },
60
59
  "peerDependencies": {
61
60
  "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0",
62
61
  "babel-plugin-react-compiler": "^1.0.0",
63
- "oxc-transform-react": "^0.145.0",
62
+ "oxc-transform-react": "^0.152.0",
64
63
  "vite": "^8.0.0"
65
64
  },
66
65
  "peerDependenciesMeta": {