@vitejs/plugin-react 4.6.0 → 5.0.4

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
@@ -21,7 +21,7 @@ export default defineConfig({
21
21
 
22
22
  ### include/exclude
23
23
 
24
- Includes `.js`, `.jsx`, `.ts` & `.tsx` by default. This option can be used to add fast refresh to `.mdx` files:
24
+ Includes `.js`, `.jsx`, `.ts` & `.tsx` and excludes `/node_modules/` by default. This option can be used to add fast refresh to `.mdx` files:
25
25
 
26
26
  ```js
27
27
  import { defineConfig } from 'vite'
@@ -36,11 +36,9 @@ export default defineConfig({
36
36
  })
37
37
  ```
38
38
 
39
- > `node_modules` are never processed by this plugin (but esbuild will)
40
-
41
39
  ### jsxImportSource
42
40
 
43
- Control where the JSX factory is imported from. Default to `'react'`
41
+ Control where the JSX factory is imported from. By default, this is inferred from `jsxImportSource` from corresponding a tsconfig file for a transformed file.
44
42
 
45
43
  ```js
46
44
  react({ jsxImportSource: '@emotion/react' })
@@ -129,10 +127,6 @@ Otherwise, you'll probably get this error:
129
127
  Uncaught Error: @vitejs/plugin-react can't detect preamble. Something is wrong.
130
128
  ```
131
129
 
132
- ### disableOxcRecommendation
133
-
134
- If set, disables the recommendation to use `@vitejs/plugin-react-oxc` (which is shown when `rolldown-vite` is detected and `babel` is not configured).
135
-
136
130
  ## Consistent components exports
137
131
 
138
132
  For React refresh to work correctly, your file should only export React components. You can find a good explanation in the [Gatsby docs](https://www.gatsbyjs.com/docs/reference/local-development/fast-refresh/#how-it-works).
package/dist/index.d.ts CHANGED
@@ -1,38 +1,35 @@
1
- import { TransformOptions, ParserOptions } from '@babel/core';
2
- import { PluginOption, ResolvedConfig } from 'vite';
1
+ import { Plugin, ResolvedConfig } from "vite";
2
+ import { ParserOptions, TransformOptions } from "@babel/core";
3
3
 
4
+ //#region src/index.d.ts
4
5
  interface Options {
5
- include?: string | RegExp | Array<string | RegExp>;
6
- exclude?: string | RegExp | Array<string | RegExp>;
7
- /**
8
- * Control where the JSX factory is imported from.
9
- * https://esbuild.github.io/api/#jsx-import-source
10
- * @default 'react'
11
- */
12
- jsxImportSource?: string;
13
- /**
14
- * Note: Skipping React import with classic runtime is not supported from v4
15
- * @default "automatic"
16
- */
17
- jsxRuntime?: 'classic' | 'automatic';
18
- /**
19
- * Babel configuration applied in both dev and prod.
20
- */
21
- babel?: BabelOptions | ((id: string, options: {
22
- ssr?: boolean;
23
- }) => BabelOptions);
24
- /**
25
- * React Fast Refresh runtime URL prefix.
26
- * Useful in a module federation context to enable HMR by specifying
27
- * the host application URL in the Vite config of a remote application.
28
- * @example
29
- * reactRefreshHost: 'http://localhost:3000'
30
- */
31
- reactRefreshHost?: string;
32
- /**
33
- * If set, disables the recommendation to use `@vitejs/plugin-react-oxc`
34
- */
35
- disableOxcRecommendation?: boolean;
6
+ include?: string | RegExp | Array<string | RegExp>;
7
+ exclude?: string | RegExp | Array<string | RegExp>;
8
+ /**
9
+ * Control where the JSX factory is imported from.
10
+ * https://esbuild.github.io/api/#jsx-import-source
11
+ * @default 'react'
12
+ */
13
+ jsxImportSource?: string;
14
+ /**
15
+ * Note: Skipping React import with classic runtime is not supported from v4
16
+ * @default "automatic"
17
+ */
18
+ jsxRuntime?: 'classic' | 'automatic';
19
+ /**
20
+ * Babel configuration applied in both dev and prod.
21
+ */
22
+ babel?: BabelOptions | ((id: string, options: {
23
+ ssr?: boolean;
24
+ }) => BabelOptions);
25
+ /**
26
+ * React Fast Refresh runtime URL prefix.
27
+ * Useful in a module federation context to enable HMR by specifying
28
+ * the host application URL in the Vite config of a remote application.
29
+ * @example
30
+ * reactRefreshHost: 'http://localhost:3000'
31
+ */
32
+ reactRefreshHost?: string;
36
33
  }
37
34
  type BabelOptions = Omit<TransformOptions, 'ast' | 'filename' | 'root' | 'sourceFileName' | 'sourceMaps' | 'inputSourceMap'>;
38
35
  /**
@@ -40,28 +37,28 @@ type BabelOptions = Omit<TransformOptions, 'ast' | 'filename' | 'root' | 'source
40
37
  * an `api.reactBabel` method.
41
38
  */
42
39
  interface ReactBabelOptions extends BabelOptions {
43
- plugins: Extract<BabelOptions['plugins'], any[]>;
44
- presets: Extract<BabelOptions['presets'], any[]>;
45
- overrides: Extract<BabelOptions['overrides'], any[]>;
46
- parserOpts: ParserOptions & {
47
- plugins: Extract<ParserOptions['plugins'], any[]>;
48
- };
40
+ plugins: Extract<BabelOptions['plugins'], any[]>;
41
+ presets: Extract<BabelOptions['presets'], any[]>;
42
+ overrides: Extract<BabelOptions['overrides'], any[]>;
43
+ parserOpts: ParserOptions & {
44
+ plugins: Extract<ParserOptions['plugins'], any[]>;
45
+ };
49
46
  }
50
47
  type ReactBabelHook = (babelConfig: ReactBabelOptions, context: ReactBabelHookContext, config: ResolvedConfig) => void;
51
48
  type ReactBabelHookContext = {
52
- ssr: boolean;
53
- id: string;
49
+ ssr: boolean;
50
+ id: string;
54
51
  };
55
52
  type ViteReactPluginApi = {
56
- /**
57
- * Manipulate the Babel options of `@vitejs/plugin-react`
58
- */
59
- reactBabel?: ReactBabelHook;
53
+ /**
54
+ * Manipulate the Babel options of `@vitejs/plugin-react`
55
+ */
56
+ reactBabel?: ReactBabelHook;
60
57
  };
61
- declare function viteReact(opts?: Options): PluginOption[];
58
+ declare function viteReact(opts?: Options): Plugin[];
62
59
  declare namespace viteReact {
63
- var preambleCode: string;
60
+ var preambleCode: string;
64
61
  }
65
-
66
- export = viteReact;
67
- export type { BabelOptions, Options, ReactBabelOptions, ViteReactPluginApi };
62
+ declare function viteReactForCjs(this: unknown, options: Options): Plugin[];
63
+ //#endregion
64
+ export { BabelOptions, Options, ReactBabelOptions, ViteReactPluginApi, viteReact as default, viteReactForCjs as "module.exports" };
package/dist/index.js ADDED
@@ -0,0 +1,377 @@
1
+ import { dirname, join } from "node:path";
2
+ import { fileURLToPath } from "node:url";
3
+ import { readFileSync } from "node:fs";
4
+ import * as vite from "vite";
5
+ import { createFilter } from "vite";
6
+ import { exactRegex, makeIdFiltersToMatchWithQuery } from "@rolldown/pluginutils";
7
+
8
+ //#region ../common/refresh-utils.ts
9
+ const runtimePublicPath = "/@react-refresh";
10
+ const reactCompRE = /extends\s+(?:React\.)?(?:Pure)?Component/;
11
+ const refreshContentRE = /\$RefreshReg\$\(/;
12
+ const preambleCode = `import { injectIntoGlobalHook } from "__BASE__${runtimePublicPath.slice(1)}";
13
+ injectIntoGlobalHook(window);
14
+ window.$RefreshReg$ = () => {};
15
+ window.$RefreshSig$ = () => (type) => type;`;
16
+ const getPreambleCode = (base) => preambleCode.replace("__BASE__", base);
17
+ function addRefreshWrapper(code, pluginName, id, reactRefreshHost = "") {
18
+ const hasRefresh = refreshContentRE.test(code);
19
+ const onlyReactComp = !hasRefresh && reactCompRE.test(code);
20
+ if (!hasRefresh && !onlyReactComp) return void 0;
21
+ let newCode = code;
22
+ newCode += `
23
+
24
+ import * as RefreshRuntime from "${reactRefreshHost}${runtimePublicPath}";
25
+ const inWebWorker = typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope;
26
+ if (import.meta.hot && !inWebWorker) {
27
+ if (!window.$RefreshReg$) {
28
+ throw new Error(
29
+ "${pluginName} can't detect preamble. Something is wrong."
30
+ );
31
+ }
32
+
33
+ RefreshRuntime.__hmr_import(import.meta.url).then((currentExports) => {
34
+ RefreshRuntime.registerExportsForReactRefresh(${JSON.stringify(id)}, currentExports);
35
+ import.meta.hot.accept((nextExports) => {
36
+ if (!nextExports) return;
37
+ const invalidateMessage = RefreshRuntime.validateRefreshBoundaryAndEnqueueUpdate(${JSON.stringify(id)}, currentExports, nextExports);
38
+ if (invalidateMessage) import.meta.hot.invalidate(invalidateMessage);
39
+ });
40
+ });
41
+ }
42
+ `;
43
+ if (hasRefresh) newCode += `function $RefreshReg$(type, id) { return RefreshRuntime.register(type, ${JSON.stringify(id)} + ' ' + id) }
44
+ function $RefreshSig$() { return RefreshRuntime.createSignatureFunctionForTransform(); }
45
+ `;
46
+ return newCode;
47
+ }
48
+
49
+ //#endregion
50
+ //#region ../common/warning.ts
51
+ const silenceUseClientWarning = (userConfig) => ({ rollupOptions: { onwarn(warning, defaultHandler) {
52
+ if (warning.code === "MODULE_LEVEL_DIRECTIVE" && (warning.message.includes("use client") || warning.message.includes("use server"))) return;
53
+ if (warning.code === "SOURCEMAP_ERROR" && warning.message.includes("resolve original location") && warning.pos === 0) return;
54
+ if (userConfig.build?.rollupOptions?.onwarn) userConfig.build.rollupOptions.onwarn(warning, defaultHandler);
55
+ else defaultHandler(warning);
56
+ } } });
57
+
58
+ //#endregion
59
+ //#region src/index.ts
60
+ const _dirname = dirname(fileURLToPath(import.meta.url));
61
+ const refreshRuntimePath = join(_dirname, "refresh-runtime.js");
62
+ let babel;
63
+ async function loadBabel() {
64
+ if (!babel) babel = await import("@babel/core");
65
+ return babel;
66
+ }
67
+ const defaultIncludeRE = /\.[tj]sx?$/;
68
+ const defaultExcludeRE = /\/node_modules\//;
69
+ const tsRE = /\.tsx?$/;
70
+ const compilerAnnotationRE = /['"]use memo['"]/;
71
+ function viteReact(opts = {}) {
72
+ const include = opts.include ?? defaultIncludeRE;
73
+ const exclude = opts.exclude ?? defaultExcludeRE;
74
+ const filter = createFilter(include, exclude);
75
+ const jsxImportSource = opts.jsxImportSource ?? "react";
76
+ const jsxImportRuntime = `${jsxImportSource}/jsx-runtime`;
77
+ const jsxImportDevRuntime = `${jsxImportSource}/jsx-dev-runtime`;
78
+ const isRolldownVite = "rolldownVersion" in vite;
79
+ let runningInVite = false;
80
+ let isProduction = true;
81
+ let projectRoot = process.cwd();
82
+ let skipFastRefresh = true;
83
+ let base;
84
+ let isFullBundle = false;
85
+ let runPluginOverrides;
86
+ let staticBabelOptions;
87
+ const importReactRE = /\bimport\s+(?:\*\s+as\s+)?React\b/;
88
+ const viteBabel = {
89
+ name: "vite:react-babel",
90
+ enforce: "pre",
91
+ config(_userConfig, { command }) {
92
+ if ("rolldownVersion" in vite) if (opts.jsxRuntime === "classic") return { oxc: {
93
+ jsx: {
94
+ runtime: "classic",
95
+ refresh: command === "serve",
96
+ development: false
97
+ },
98
+ jsxRefreshInclude: makeIdFiltersToMatchWithQuery(include),
99
+ jsxRefreshExclude: makeIdFiltersToMatchWithQuery(exclude)
100
+ } };
101
+ else return {
102
+ oxc: {
103
+ jsx: {
104
+ runtime: "automatic",
105
+ importSource: opts.jsxImportSource,
106
+ refresh: command === "serve"
107
+ },
108
+ jsxRefreshInclude: makeIdFiltersToMatchWithQuery(include),
109
+ jsxRefreshExclude: makeIdFiltersToMatchWithQuery(exclude)
110
+ },
111
+ optimizeDeps: { rollupOptions: { transform: { jsx: { runtime: "automatic" } } } }
112
+ };
113
+ if (opts.jsxRuntime === "classic") return { esbuild: { jsx: "transform" } };
114
+ else return {
115
+ esbuild: {
116
+ jsx: "automatic",
117
+ jsxImportSource: opts.jsxImportSource
118
+ },
119
+ optimizeDeps: { esbuildOptions: { jsx: "automatic" } }
120
+ };
121
+ },
122
+ configResolved(config) {
123
+ runningInVite = true;
124
+ base = config.base;
125
+ if (config.experimental.fullBundleMode) isFullBundle = true;
126
+ projectRoot = config.root;
127
+ isProduction = config.isProduction;
128
+ skipFastRefresh = isProduction || config.command === "build" || config.server.hmr === false;
129
+ const hooks = config.plugins.map((plugin) => plugin.api?.reactBabel).filter(defined);
130
+ if (hooks.length > 0) runPluginOverrides = (babelOptions, context) => {
131
+ hooks.forEach((hook) => hook(babelOptions, context, config));
132
+ };
133
+ else if (typeof opts.babel !== "function") {
134
+ staticBabelOptions = createBabelOptions(opts.babel);
135
+ if ((isRolldownVite || skipFastRefresh) && canSkipBabel(staticBabelOptions.plugins, staticBabelOptions) && (opts.jsxRuntime === "classic" ? isProduction : true)) delete viteBabel.transform;
136
+ }
137
+ },
138
+ options(options) {
139
+ if (!runningInVite) {
140
+ options.jsx = {
141
+ mode: opts.jsxRuntime,
142
+ importSource: opts.jsxImportSource
143
+ };
144
+ return options;
145
+ }
146
+ },
147
+ transform: {
148
+ filter: { id: {
149
+ include: makeIdFiltersToMatchWithQuery(include),
150
+ exclude: makeIdFiltersToMatchWithQuery(exclude)
151
+ } },
152
+ async handler(code, id, options) {
153
+ const [filepath] = id.split("?");
154
+ if (!filter(filepath)) return;
155
+ const ssr = options?.ssr === true;
156
+ const babelOptions = (() => {
157
+ if (staticBabelOptions) return staticBabelOptions;
158
+ const newBabelOptions = createBabelOptions(typeof opts.babel === "function" ? opts.babel(id, { ssr }) : opts.babel);
159
+ runPluginOverrides?.(newBabelOptions, {
160
+ id,
161
+ ssr
162
+ });
163
+ return newBabelOptions;
164
+ })();
165
+ const plugins = [...babelOptions.plugins];
166
+ let reactCompilerPlugin$1 = getReactCompilerPlugin(plugins);
167
+ if (reactCompilerPlugin$1 && ssr) {
168
+ plugins.splice(plugins.indexOf(reactCompilerPlugin$1), 1);
169
+ reactCompilerPlugin$1 = void 0;
170
+ }
171
+ if (Array.isArray(reactCompilerPlugin$1) && reactCompilerPlugin$1[1]?.compilationMode === "annotation" && !compilerAnnotationRE.test(code)) {
172
+ plugins.splice(plugins.indexOf(reactCompilerPlugin$1), 1);
173
+ reactCompilerPlugin$1 = void 0;
174
+ }
175
+ const isJSX = filepath.endsWith("x");
176
+ const useFastRefresh = !(isRolldownVite || skipFastRefresh) && !ssr && (isJSX || (opts.jsxRuntime === "classic" ? importReactRE.test(code) : code.includes(jsxImportDevRuntime) || code.includes(jsxImportRuntime)));
177
+ if (useFastRefresh) plugins.push([await loadPlugin("react-refresh/babel"), { skipEnvCheck: true }]);
178
+ if (opts.jsxRuntime === "classic" && isJSX) {
179
+ if (!isProduction) plugins.push(await loadPlugin("@babel/plugin-transform-react-jsx-self"), await loadPlugin("@babel/plugin-transform-react-jsx-source"));
180
+ }
181
+ if (canSkipBabel(plugins, babelOptions)) return;
182
+ const parserPlugins = [...babelOptions.parserOpts.plugins];
183
+ if (!filepath.endsWith(".ts")) parserPlugins.push("jsx");
184
+ if (tsRE.test(filepath)) parserPlugins.push("typescript");
185
+ const result = await (await loadBabel()).transformAsync(code, {
186
+ ...babelOptions,
187
+ root: projectRoot,
188
+ filename: id,
189
+ sourceFileName: filepath,
190
+ retainLines: reactCompilerPlugin$1 ? false : !isProduction && isJSX && opts.jsxRuntime !== "classic",
191
+ parserOpts: {
192
+ ...babelOptions.parserOpts,
193
+ sourceType: "module",
194
+ allowAwaitOutsideFunction: true,
195
+ plugins: parserPlugins
196
+ },
197
+ generatorOpts: {
198
+ ...babelOptions.generatorOpts,
199
+ importAttributesKeyword: "with",
200
+ decoratorsBeforeExport: true
201
+ },
202
+ plugins,
203
+ sourceMaps: true
204
+ });
205
+ if (result) {
206
+ if (!useFastRefresh) return {
207
+ code: result.code,
208
+ map: result.map
209
+ };
210
+ return {
211
+ code: addRefreshWrapper(result.code, "@vitejs/plugin-react", id, opts.reactRefreshHost) ?? result.code,
212
+ map: result.map
213
+ };
214
+ }
215
+ }
216
+ }
217
+ };
218
+ const viteRefreshWrapper = {
219
+ name: "vite:react:refresh-wrapper",
220
+ apply: "serve",
221
+ async applyToEnvironment(env) {
222
+ if (env.config.consumer !== "client" || skipFastRefresh) return false;
223
+ let nativePlugin;
224
+ try {
225
+ nativePlugin = (await import("vite/internal")).reactRefreshWrapperPlugin;
226
+ } catch {}
227
+ if (!nativePlugin || [
228
+ "7.1.10",
229
+ "7.1.11",
230
+ "7.1.12"
231
+ ].includes(vite.version)) return true;
232
+ delete viteRefreshWrapper.transform;
233
+ return nativePlugin({
234
+ cwd: process.cwd(),
235
+ include: makeIdFiltersToMatchWithQuery(include),
236
+ exclude: makeIdFiltersToMatchWithQuery(exclude),
237
+ jsxImportSource,
238
+ reactRefreshHost: opts.reactRefreshHost ?? ""
239
+ });
240
+ },
241
+ transform: {
242
+ filter: { id: {
243
+ include: makeIdFiltersToMatchWithQuery(include),
244
+ exclude: makeIdFiltersToMatchWithQuery(exclude)
245
+ } },
246
+ handler(code, id, options) {
247
+ const ssr = options?.ssr === true;
248
+ const [filepath] = id.split("?");
249
+ const isJSX = filepath.endsWith("x");
250
+ if (!(!skipFastRefresh && !ssr && (isJSX || code.includes(jsxImportDevRuntime) || code.includes(jsxImportRuntime)))) return;
251
+ const newCode = addRefreshWrapper(code, "@vitejs/plugin-react", id, opts.reactRefreshHost);
252
+ return newCode ? {
253
+ code: newCode,
254
+ map: null
255
+ } : void 0;
256
+ }
257
+ }
258
+ };
259
+ const viteConfigPost = {
260
+ name: "vite:react:config-post",
261
+ enforce: "post",
262
+ config(userConfig) {
263
+ if (userConfig.server?.hmr === false) return { oxc: { jsx: { refresh: false } } };
264
+ }
265
+ };
266
+ const viteReactRefreshFullBundleMode = {
267
+ name: "vite:react-refresh-fbm",
268
+ enforce: "pre",
269
+ transformIndexHtml: {
270
+ handler() {
271
+ if (!skipFastRefresh && isFullBundle) return [{
272
+ tag: "script",
273
+ attrs: { type: "module" },
274
+ children: getPreambleCode(base)
275
+ }];
276
+ },
277
+ order: "pre"
278
+ }
279
+ };
280
+ const dependencies = [
281
+ "react",
282
+ "react-dom",
283
+ jsxImportDevRuntime,
284
+ jsxImportRuntime
285
+ ];
286
+ const staticBabelPlugins = typeof opts.babel === "object" ? opts.babel?.plugins ?? [] : [];
287
+ const reactCompilerPlugin = getReactCompilerPlugin(staticBabelPlugins);
288
+ if (reactCompilerPlugin != null) {
289
+ const reactCompilerRuntimeModule = getReactCompilerRuntimeModule(reactCompilerPlugin);
290
+ dependencies.push(reactCompilerRuntimeModule);
291
+ }
292
+ const viteReactRefresh = {
293
+ name: "vite:react-refresh",
294
+ enforce: "pre",
295
+ config: (userConfig) => ({
296
+ build: silenceUseClientWarning(userConfig),
297
+ optimizeDeps: { include: dependencies }
298
+ }),
299
+ resolveId: {
300
+ filter: { id: exactRegex(runtimePublicPath) },
301
+ handler(id) {
302
+ if (id === runtimePublicPath) return id;
303
+ }
304
+ },
305
+ load: {
306
+ filter: { id: exactRegex(runtimePublicPath) },
307
+ handler(id) {
308
+ if (id === runtimePublicPath) return readFileSync(refreshRuntimePath, "utf-8").replace(/__README_URL__/g, "https://github.com/vitejs/vite-plugin-react/tree/main/packages/plugin-react");
309
+ }
310
+ },
311
+ transformIndexHtml() {
312
+ if (!skipFastRefresh && !isFullBundle) return [{
313
+ tag: "script",
314
+ attrs: { type: "module" },
315
+ children: getPreambleCode(base)
316
+ }];
317
+ }
318
+ };
319
+ return [
320
+ viteBabel,
321
+ ...isRolldownVite ? [
322
+ viteRefreshWrapper,
323
+ viteConfigPost,
324
+ viteReactRefreshFullBundleMode
325
+ ] : [],
326
+ viteReactRefresh
327
+ ];
328
+ }
329
+ viteReact.preambleCode = preambleCode;
330
+ function viteReactForCjs(options) {
331
+ return viteReact.call(this, options);
332
+ }
333
+ Object.assign(viteReactForCjs, { default: viteReactForCjs });
334
+ function canSkipBabel(plugins, babelOptions) {
335
+ return !(plugins.length || babelOptions.presets.length || babelOptions.configFile || babelOptions.babelrc);
336
+ }
337
+ const loadedPlugin = /* @__PURE__ */ new Map();
338
+ function loadPlugin(path) {
339
+ const cached = loadedPlugin.get(path);
340
+ if (cached) return cached;
341
+ const promise = import(path).then((module) => {
342
+ const value = module.default || module;
343
+ loadedPlugin.set(path, value);
344
+ return value;
345
+ });
346
+ loadedPlugin.set(path, promise);
347
+ return promise;
348
+ }
349
+ function createBabelOptions(rawOptions) {
350
+ const babelOptions = {
351
+ babelrc: false,
352
+ configFile: false,
353
+ ...rawOptions
354
+ };
355
+ babelOptions.plugins ||= [];
356
+ babelOptions.presets ||= [];
357
+ babelOptions.overrides ||= [];
358
+ babelOptions.parserOpts ||= {};
359
+ babelOptions.parserOpts.plugins ||= [];
360
+ return babelOptions;
361
+ }
362
+ function defined(value) {
363
+ return value !== void 0;
364
+ }
365
+ function getReactCompilerPlugin(plugins) {
366
+ return plugins.find((p) => p === "babel-plugin-react-compiler" || Array.isArray(p) && p[0] === "babel-plugin-react-compiler");
367
+ }
368
+ function getReactCompilerRuntimeModule(plugin) {
369
+ let moduleName = "react/compiler-runtime";
370
+ if (Array.isArray(plugin)) {
371
+ if (plugin[1]?.target === "17" || plugin[1]?.target === "18") moduleName = "react-compiler-runtime";
372
+ }
373
+ return moduleName;
374
+ }
375
+
376
+ //#endregion
377
+ export { viteReact as default, viteReactForCjs as "module.exports" };
@@ -243,7 +243,7 @@ function performReactRefresh() {
243
243
  }
244
244
  }
245
245
 
246
- function register(type, id) {
246
+ export function register(type, id) {
247
247
  if (type === null) {
248
248
  return
249
249
  }
@@ -545,14 +545,25 @@ function isLikelyComponentType(type) {
545
545
  }
546
546
  }
547
547
 
548
+ function isCompoundComponent(type) {
549
+ if (!isPlainObject(type)) return false
550
+ for (const key in type) {
551
+ if (!isLikelyComponentType(type[key])) return false
552
+ }
553
+ return true
554
+ }
555
+
556
+ function isPlainObject(obj) {
557
+ return (
558
+ Object.prototype.toString.call(obj) === '[object Object]' &&
559
+ (obj.constructor === Object || obj.constructor === undefined)
560
+ )
561
+ }
562
+
548
563
  /**
549
564
  * Plugin utils
550
565
  */
551
566
 
552
- export function getRefreshReg(filename) {
553
- return (type, id) => register(type, filename + ' ' + id)
554
- }
555
-
556
567
  // Taken from https://github.com/pmmmwh/react-refresh-webpack-plugin/blob/main/lib/runtime/RefreshUtils.js#L141
557
568
  // This allows to resister components not detected by SWC like styled component
558
569
  export function registerExportsForReactRefresh(filename, moduleExports) {
@@ -565,6 +576,13 @@ export function registerExportsForReactRefresh(filename, moduleExports) {
565
576
  // The register function has an identity check to not register twice the same component,
566
577
  // so this is safe to not used the same key here.
567
578
  register(exportValue, filename + ' export ' + key)
579
+ } else if (isCompoundComponent(exportValue)) {
580
+ for (const subKey in exportValue) {
581
+ register(
582
+ exportValue[subKey],
583
+ filename + ' export ' + key + '-' + subKey,
584
+ )
585
+ }
568
586
  }
569
587
  }
570
588
  }
@@ -618,6 +636,7 @@ export function validateRefreshBoundaryAndEnqueueUpdate(
618
636
  (key, value) => {
619
637
  hasExports = true
620
638
  if (isLikelyComponentType(value)) return true
639
+ if (isCompoundComponent(value)) return true
621
640
  return prevExports[key] === nextExports[key]
622
641
  },
623
642
  )
@@ -630,10 +649,7 @@ export function validateRefreshBoundaryAndEnqueueUpdate(
630
649
 
631
650
  function predicateOnExport(ignoredExports, moduleExports, predicate) {
632
651
  for (const key in moduleExports) {
633
- if (key === '__esModule') continue
634
652
  if (ignoredExports.includes(key)) continue
635
- const desc = Object.getOwnPropertyDescriptor(moduleExports, key)
636
- if (desc && desc.get) return key
637
653
  if (!predicate(key, moduleExports[key])) return key
638
654
  }
639
655
  return true
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vitejs/plugin-react",
3
- "version": "4.6.0",
3
+ "version": "5.0.4",
4
4
  "license": "MIT",
5
5
  "author": "Evan You",
6
6
  "description": "The default Vite plugin for React projects",
@@ -20,24 +20,15 @@
20
20
  "dist"
21
21
  ],
22
22
  "type": "module",
23
- "main": "./dist/index.cjs",
24
- "module": "./dist/index.mjs",
25
- "types": "./dist/index.d.mts",
26
- "exports": {
27
- ".": {
28
- "import": "./dist/index.mjs",
29
- "require": "./dist/index.cjs"
30
- }
31
- },
23
+ "exports": "./dist/index.js",
32
24
  "scripts": {
33
- "dev": "unbuild --stub",
34
- "build": "unbuild && pnpm run patch-cjs && tsx scripts/copyRefreshRuntime.ts",
35
- "patch-cjs": "tsx ../../scripts/patchCJS.ts",
25
+ "dev": "tsdown --watch ./src --watch ../common",
26
+ "build": "tsdown",
36
27
  "prepublishOnly": "npm run build",
37
28
  "test-unit": "vitest run"
38
29
  },
39
30
  "engines": {
40
- "node": "^14.18.0 || >=16.0.0"
31
+ "node": "^20.19.0 || >=22.12.0"
41
32
  },
42
33
  "repository": {
43
34
  "type": "git",
@@ -49,23 +40,23 @@
49
40
  },
50
41
  "homepage": "https://github.com/vitejs/vite-plugin-react/tree/main/packages/plugin-react#readme",
51
42
  "dependencies": {
52
- "@babel/core": "^7.27.4",
43
+ "@babel/core": "^7.28.4",
53
44
  "@babel/plugin-transform-react-jsx-self": "^7.27.1",
54
45
  "@babel/plugin-transform-react-jsx-source": "^7.27.1",
55
- "@rolldown/pluginutils": "1.0.0-beta.19",
46
+ "@rolldown/pluginutils": "1.0.0-beta.38",
56
47
  "@types/babel__core": "^7.20.5",
57
48
  "react-refresh": "^0.17.0"
58
49
  },
59
50
  "peerDependencies": {
60
- "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0"
51
+ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
61
52
  },
62
53
  "devDependencies": {
63
54
  "@vitejs/react-common": "workspace:*",
64
- "babel-plugin-react-compiler": "19.1.0-rc.2",
65
- "react": "^19.1.0",
66
- "react-dom": "^19.1.0",
67
- "rolldown": "1.0.0-beta.19",
68
- "unbuild": "^3.5.0",
55
+ "babel-plugin-react-compiler": "19.1.0-rc.3",
56
+ "react": "^19.1.1",
57
+ "react-dom": "^19.1.1",
58
+ "rolldown": "1.0.0-beta.38",
59
+ "tsdown": "^0.15.4",
69
60
  "vitest": "^3.2.4"
70
61
  }
71
62
  }