@vitejs/plugin-react 1.3.2 → 2.0.0-alpha.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/dist/index.mjs ADDED
@@ -0,0 +1,387 @@
1
+ import path from 'path';
2
+ import * as babel from '@babel/core';
3
+ import { createFilter } from '@rollup/pluginutils';
4
+ import { normalizePath } from 'vite';
5
+ import fs from 'fs';
6
+ import { createRequire } from 'module';
7
+
8
+ const runtimePublicPath = "/@react-refresh";
9
+ const _require = createRequire(import.meta.url);
10
+ const reactRefreshDir = path.dirname(_require.resolve("react-refresh/package.json"));
11
+ const runtimeFilePath = path.join(reactRefreshDir, "cjs/react-refresh-runtime.development.js");
12
+ const runtimeCode = `
13
+ const exports = {}
14
+ ${fs.readFileSync(runtimeFilePath, "utf-8")}
15
+ function debounce(fn, delay) {
16
+ let handle
17
+ return () => {
18
+ clearTimeout(handle)
19
+ handle = setTimeout(fn, delay)
20
+ }
21
+ }
22
+ exports.performReactRefresh = debounce(exports.performReactRefresh, 16)
23
+ export default exports
24
+ `;
25
+ const preambleCode = `
26
+ import RefreshRuntime from "__BASE__${runtimePublicPath.slice(1)}"
27
+ RefreshRuntime.injectIntoGlobalHook(window)
28
+ window.$RefreshReg$ = () => {}
29
+ window.$RefreshSig$ = () => (type) => type
30
+ window.__vite_plugin_react_preamble_installed__ = true
31
+ `;
32
+ const header = `
33
+ import RefreshRuntime from "${runtimePublicPath}";
34
+
35
+ let prevRefreshReg;
36
+ let prevRefreshSig;
37
+
38
+ if (import.meta.hot) {
39
+ if (!window.__vite_plugin_react_preamble_installed__) {
40
+ throw new Error(
41
+ "@vitejs/plugin-react can't detect preamble. Something is wrong. " +
42
+ "See https://github.com/vitejs/vite-plugin-react/pull/11#discussion_r430879201"
43
+ );
44
+ }
45
+
46
+ prevRefreshReg = window.$RefreshReg$;
47
+ prevRefreshSig = window.$RefreshSig$;
48
+ window.$RefreshReg$ = (type, id) => {
49
+ RefreshRuntime.register(type, __SOURCE__ + " " + id)
50
+ };
51
+ window.$RefreshSig$ = RefreshRuntime.createSignatureFunctionForTransform;
52
+ }`.replace(/[\n]+/gm, "");
53
+ const footer = `
54
+ if (import.meta.hot) {
55
+ window.$RefreshReg$ = prevRefreshReg;
56
+ window.$RefreshSig$ = prevRefreshSig;
57
+
58
+ __ACCEPT__
59
+ if (!window.__vite_plugin_react_timeout) {
60
+ window.__vite_plugin_react_timeout = setTimeout(() => {
61
+ window.__vite_plugin_react_timeout = 0;
62
+ RefreshRuntime.performReactRefresh();
63
+ }, 30);
64
+ }
65
+ }`;
66
+ function addRefreshWrapper(code, id, accept) {
67
+ return header.replace("__SOURCE__", JSON.stringify(id)) + code + footer.replace("__ACCEPT__", accept ? "import.meta.hot.accept();" : "");
68
+ }
69
+ function isRefreshBoundary(ast) {
70
+ return ast.program.body.every((node) => {
71
+ if (node.type !== "ExportNamedDeclaration") {
72
+ return true;
73
+ }
74
+ const { declaration, specifiers } = node;
75
+ if (declaration) {
76
+ if (declaration.type === "VariableDeclaration") {
77
+ return declaration.declarations.every((variable) => isComponentLikeIdentifier(variable.id));
78
+ }
79
+ if (declaration.type === "FunctionDeclaration") {
80
+ return !!declaration.id && isComponentLikeIdentifier(declaration.id);
81
+ }
82
+ }
83
+ return specifiers.every((spec) => {
84
+ return isComponentLikeIdentifier(spec.exported);
85
+ });
86
+ });
87
+ }
88
+ function isComponentLikeIdentifier(node) {
89
+ return node.type === "Identifier" && isComponentLikeName(node.name);
90
+ }
91
+ function isComponentLikeName(name) {
92
+ return typeof name === "string" && name[0] >= "A" && name[0] <= "Z";
93
+ }
94
+
95
+ function babelImportToRequire({ types: t }) {
96
+ return {
97
+ visitor: {
98
+ ImportDeclaration(path) {
99
+ const decl = path.node;
100
+ const spec = decl.specifiers[0];
101
+ path.replaceWith(t.variableDeclaration("var", [
102
+ t.variableDeclarator(spec.local, t.memberExpression(t.callExpression(t.identifier("require"), [decl.source]), spec.imported))
103
+ ]));
104
+ }
105
+ }
106
+ };
107
+ }
108
+
109
+ let babelRestoreJSX;
110
+ const jsxNotFound = [null, false];
111
+ async function getBabelRestoreJSX() {
112
+ if (!babelRestoreJSX)
113
+ babelRestoreJSX = import('./chunks/babel-restore-jsx.mjs').then((r) => {
114
+ const fn = r.default;
115
+ if ("default" in fn)
116
+ return fn.default;
117
+ return fn;
118
+ });
119
+ return babelRestoreJSX;
120
+ }
121
+ async function restoreJSX(babel, code, filename) {
122
+ const [reactAlias, isCommonJS] = parseReactAlias(code);
123
+ if (!reactAlias) {
124
+ return jsxNotFound;
125
+ }
126
+ let hasCompiledJsx = false;
127
+ const fragmentPattern = `\\b${reactAlias}\\.Fragment\\b`;
128
+ const createElementPattern = `\\b${reactAlias}\\.createElement\\(\\s*([A-Z"'][\\w$.]*["']?)`;
129
+ code = code.replace(new RegExp(fragmentPattern, "g"), () => {
130
+ hasCompiledJsx = true;
131
+ return "React.Fragment";
132
+ }).replace(new RegExp(createElementPattern, "g"), (original, component) => {
133
+ if (/^[a-z][\w$]*$/.test(component)) {
134
+ return original;
135
+ }
136
+ hasCompiledJsx = true;
137
+ return "React.createElement(" + (component === "Fragment" ? "React.Fragment" : component);
138
+ });
139
+ if (!hasCompiledJsx) {
140
+ return jsxNotFound;
141
+ }
142
+ const result = await babel.transformAsync(code, {
143
+ babelrc: false,
144
+ configFile: false,
145
+ ast: true,
146
+ code: false,
147
+ filename,
148
+ parserOpts: {
149
+ plugins: ["jsx"]
150
+ },
151
+ plugins: [await getBabelRestoreJSX()]
152
+ });
153
+ return [result?.ast, isCommonJS];
154
+ }
155
+ function parseReactAlias(code) {
156
+ let match = code.match(/\b(var|let|const) +(\w+) *= *require\(["']react["']\)/);
157
+ if (match) {
158
+ return [match[2], true];
159
+ }
160
+ match = code.match(/^import (\w+).+? from ["']react["']/m);
161
+ if (match) {
162
+ return [match[1], false];
163
+ }
164
+ return [void 0, false];
165
+ }
166
+
167
+ function viteReact(opts = {}) {
168
+ let base = "/";
169
+ let resolvedCacheDir;
170
+ let filter = createFilter(opts.include, opts.exclude);
171
+ let isProduction = true;
172
+ let projectRoot = process.cwd();
173
+ let skipFastRefresh = opts.fastRefresh === false;
174
+ let skipReactImport = false;
175
+ let runPluginOverrides = (options, context) => false;
176
+ let staticBabelOptions;
177
+ const useAutomaticRuntime = opts.jsxRuntime !== "classic";
178
+ const importReactRE = /(^|\n)import\s+(\*\s+as\s+)?React(,|\s+)/;
179
+ const fileExtensionRE = /\.[^\/\s\?]+$/;
180
+ const viteBabel = {
181
+ name: "vite:react-babel",
182
+ enforce: "pre",
183
+ configResolved(config) {
184
+ base = config.base;
185
+ projectRoot = config.root;
186
+ resolvedCacheDir = normalizePath(path.resolve(config.cacheDir));
187
+ filter = createFilter(opts.include, opts.exclude, {
188
+ resolve: projectRoot
189
+ });
190
+ isProduction = config.isProduction;
191
+ skipFastRefresh || (skipFastRefresh = isProduction || config.command === "build");
192
+ const jsxInject = config.esbuild && config.esbuild.jsxInject;
193
+ if (jsxInject && importReactRE.test(jsxInject)) {
194
+ skipReactImport = true;
195
+ config.logger.warn("[@vitejs/plugin-react] This plugin imports React for you automatically, so you can stop using `esbuild.jsxInject` for that purpose.");
196
+ }
197
+ config.plugins.forEach((plugin) => {
198
+ const hasConflict = plugin.name === "react-refresh" || plugin !== viteReactJsx && plugin.name === "vite:react-jsx";
199
+ if (hasConflict)
200
+ return config.logger.warn(`[@vitejs/plugin-react] You should stop using "${plugin.name}" since this plugin conflicts with it.`);
201
+ });
202
+ runPluginOverrides = (babelOptions, context) => {
203
+ const hooks = config.plugins.map((plugin) => plugin.api?.reactBabel).filter(Boolean);
204
+ if (hooks.length > 0) {
205
+ return (runPluginOverrides = (babelOptions2) => {
206
+ hooks.forEach((hook) => hook(babelOptions2, context, config));
207
+ return true;
208
+ })(babelOptions);
209
+ }
210
+ runPluginOverrides = () => false;
211
+ return false;
212
+ };
213
+ },
214
+ async transform(code, id, options) {
215
+ const ssr = typeof options === "boolean" ? options : options?.ssr === true;
216
+ const [filepath, querystring = ""] = id.split("?");
217
+ const [extension = ""] = querystring.match(fileExtensionRE) || filepath.match(fileExtensionRE) || [];
218
+ if (/\.(mjs|[tj]sx?)$/.test(extension)) {
219
+ const isJSX = extension.endsWith("x");
220
+ const isNodeModules = id.includes("/node_modules/");
221
+ const isProjectFile = !isNodeModules && (id[0] === "\0" || id.startsWith(projectRoot + "/"));
222
+ let babelOptions = staticBabelOptions;
223
+ if (typeof opts.babel === "function") {
224
+ const rawOptions = opts.babel(id, { ssr });
225
+ babelOptions = createBabelOptions(rawOptions);
226
+ runPluginOverrides(babelOptions, { ssr, id });
227
+ } else if (!babelOptions) {
228
+ babelOptions = createBabelOptions(opts.babel);
229
+ if (!runPluginOverrides(babelOptions, { ssr, id })) {
230
+ staticBabelOptions = babelOptions;
231
+ }
232
+ }
233
+ const plugins = isProjectFile ? [...babelOptions.plugins] : [];
234
+ let useFastRefresh = false;
235
+ if (!skipFastRefresh && !ssr && !isNodeModules) {
236
+ const isReactModule = isJSX || importReactRE.test(code);
237
+ if (isReactModule && filter(id)) {
238
+ useFastRefresh = true;
239
+ plugins.push([
240
+ await loadPlugin("react-refresh/babel"),
241
+ { skipEnvCheck: true }
242
+ ]);
243
+ }
244
+ }
245
+ let ast;
246
+ if (!isProjectFile || isJSX) {
247
+ if (useAutomaticRuntime) {
248
+ const isOptimizedReactDom = id.startsWith(resolvedCacheDir) && id.includes("/react-dom.js");
249
+ const [restoredAst, isCommonJS] = !isProjectFile && !isJSX && !isOptimizedReactDom ? await restoreJSX(babel, code, id) : [null, false];
250
+ if (isJSX || (ast = restoredAst)) {
251
+ plugins.push([
252
+ await loadPlugin("@babel/plugin-transform-react-jsx" + (isProduction ? "" : "-development")),
253
+ {
254
+ runtime: "automatic",
255
+ importSource: opts.jsxImportSource,
256
+ pure: opts.jsxPure !== false
257
+ }
258
+ ]);
259
+ if (isCommonJS) {
260
+ plugins.push(babelImportToRequire);
261
+ }
262
+ }
263
+ } else if (isProjectFile) {
264
+ if (!isProduction) {
265
+ plugins.push(await loadPlugin("@babel/plugin-transform-react-jsx-self"), await loadPlugin("@babel/plugin-transform-react-jsx-source"));
266
+ }
267
+ if (!skipReactImport && !importReactRE.test(code)) {
268
+ code = `import React from 'react'; ` + code;
269
+ }
270
+ }
271
+ }
272
+ const shouldSkip = !plugins.length && !babelOptions.configFile && !(isProjectFile && babelOptions.babelrc);
273
+ if (shouldSkip) {
274
+ return;
275
+ }
276
+ const parserPlugins = [
277
+ ...babelOptions.parserOpts.plugins,
278
+ "importMeta",
279
+ "topLevelAwait",
280
+ "classProperties",
281
+ "classPrivateProperties",
282
+ "classPrivateMethods"
283
+ ];
284
+ if (!extension.endsWith(".ts")) {
285
+ parserPlugins.push("jsx");
286
+ }
287
+ if (/\.tsx?$/.test(extension)) {
288
+ parserPlugins.push("typescript");
289
+ }
290
+ const transformAsync = ast ? babel.transformFromAstAsync.bind(babel, ast, code) : babel.transformAsync.bind(babel, code);
291
+ const isReasonReact = extension.endsWith(".bs.js");
292
+ const result = await transformAsync({
293
+ ...babelOptions,
294
+ ast: !isReasonReact,
295
+ root: projectRoot,
296
+ filename: id,
297
+ sourceFileName: filepath,
298
+ parserOpts: {
299
+ ...babelOptions.parserOpts,
300
+ sourceType: "module",
301
+ allowAwaitOutsideFunction: true,
302
+ plugins: parserPlugins
303
+ },
304
+ generatorOpts: {
305
+ ...babelOptions.generatorOpts,
306
+ decoratorsBeforeExport: true
307
+ },
308
+ plugins,
309
+ sourceMaps: true,
310
+ inputSourceMap: false
311
+ });
312
+ if (result) {
313
+ let code2 = result.code;
314
+ if (useFastRefresh && /\$RefreshReg\$\(/.test(code2)) {
315
+ const accept = isReasonReact || isRefreshBoundary(result.ast);
316
+ code2 = addRefreshWrapper(code2, id, accept);
317
+ }
318
+ return {
319
+ code: code2,
320
+ map: result.map
321
+ };
322
+ }
323
+ }
324
+ }
325
+ };
326
+ const viteReactRefresh = {
327
+ name: "vite:react-refresh",
328
+ enforce: "pre",
329
+ config: () => ({
330
+ resolve: {
331
+ dedupe: ["react", "react-dom"]
332
+ }
333
+ }),
334
+ resolveId(id) {
335
+ if (id === runtimePublicPath) {
336
+ return id;
337
+ }
338
+ },
339
+ load(id) {
340
+ if (id === runtimePublicPath) {
341
+ return runtimeCode;
342
+ }
343
+ },
344
+ transformIndexHtml() {
345
+ if (!skipFastRefresh)
346
+ return [
347
+ {
348
+ tag: "script",
349
+ attrs: { type: "module" },
350
+ children: preambleCode.replace(`__BASE__`, base)
351
+ }
352
+ ];
353
+ }
354
+ };
355
+ const viteReactJsx = {
356
+ name: "vite:react-jsx",
357
+ enforce: "pre",
358
+ config() {
359
+ return {
360
+ optimizeDeps: {
361
+ include: ["react/jsx-dev-runtime"]
362
+ }
363
+ };
364
+ }
365
+ };
366
+ return [viteBabel, viteReactRefresh, useAutomaticRuntime && viteReactJsx];
367
+ }
368
+ viteReact.preambleCode = preambleCode;
369
+ function loadPlugin(path2) {
370
+ return import(path2).then((module) => module.default || module);
371
+ }
372
+ function createBabelOptions(rawOptions) {
373
+ var _a;
374
+ const babelOptions = {
375
+ babelrc: false,
376
+ configFile: false,
377
+ ...rawOptions
378
+ };
379
+ babelOptions.plugins || (babelOptions.plugins = []);
380
+ babelOptions.presets || (babelOptions.presets = []);
381
+ babelOptions.overrides || (babelOptions.overrides = []);
382
+ babelOptions.parserOpts || (babelOptions.parserOpts = {});
383
+ (_a = babelOptions.parserOpts).plugins || (_a.plugins = []);
384
+ return babelOptions;
385
+ }
386
+
387
+ export { viteReact as default };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vitejs/plugin-react",
3
- "version": "1.3.2",
3
+ "version": "2.0.0-alpha.2",
4
4
  "license": "MIT",
5
5
  "author": "Evan You",
6
6
  "contributors": [
@@ -10,18 +10,24 @@
10
10
  "dist",
11
11
  "src"
12
12
  ],
13
- "main": "dist/index.js",
14
- "types": "dist/index.d.ts",
13
+ "main": "./dist/index.cjs",
14
+ "module": "./dist/index.mjs",
15
+ "types": "./dist/index.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/index.d.ts",
19
+ "import": "./dist/index.mjs",
20
+ "require": "./dist/index.cjs"
21
+ }
22
+ },
15
23
  "scripts": {
16
- "dev": "tsc -p . -w --incremental",
17
- "build": "rimraf dist && run-s build-bundle build-types",
18
- "build-bundle": "esbuild src/index.ts --bundle --platform=node --target=node12 --external:@babel/* --external:@rollup/* --external:resolve --external:react-refresh/* --outfile=dist/index.js && npm run patch-dist",
19
- "patch-dist": "ts-node ../../scripts/patchEsbuildDist.ts dist/index.js viteReact",
20
- "build-types": "tsc -p . --emitDeclarationOnly --outDir temp && api-extractor run && rimraf temp",
21
- "prepublishOnly": "(cd ../vite && npm run build) && npm run build"
24
+ "dev": "unbuild --stub",
25
+ "build": "unbuild && pnpm run patch-cjs",
26
+ "patch-cjs": "esno ../../scripts/patchCJS.ts",
27
+ "prepublishOnly": "npm run build"
22
28
  },
23
29
  "engines": {
24
- "node": ">=12.0.0"
30
+ "node": ">=14.6.0"
25
31
  },
26
32
  "repository": {
27
33
  "type": "git",
@@ -33,13 +39,19 @@
33
39
  },
34
40
  "homepage": "https://github.com/vitejs/vite/tree/main/packages/plugin-react#readme",
35
41
  "dependencies": {
36
- "@babel/core": "^7.17.10",
37
- "@babel/plugin-transform-react-jsx": "^7.17.3",
42
+ "@babel/core": "^7.18.0",
43
+ "@babel/plugin-transform-react-jsx": "^7.17.12",
38
44
  "@babel/plugin-transform-react-jsx-development": "^7.16.7",
39
- "@babel/plugin-transform-react-jsx-self": "^7.16.7",
45
+ "@babel/plugin-transform-react-jsx-self": "^7.17.12",
40
46
  "@babel/plugin-transform-react-jsx-source": "^7.16.7",
41
47
  "@rollup/pluginutils": "^4.2.1",
42
48
  "react-refresh": "^0.13.0",
43
49
  "resolve": "^1.22.0"
50
+ },
51
+ "peerDependencies": {
52
+ "vite": "^3.0.0-alpha"
53
+ },
54
+ "devDependencies": {
55
+ "vite": "workspace:*"
44
56
  }
45
57
  }
@@ -1,11 +1,13 @@
1
- import type { types as t } from '@babel/core'
2
1
  import fs from 'fs'
3
2
  import path from 'path'
3
+ import { createRequire } from 'module'
4
+ import type { types as t } from '@babel/core'
4
5
 
5
6
  export const runtimePublicPath = '/@react-refresh'
6
7
 
8
+ const _require = createRequire(import.meta.url)
7
9
  const reactRefreshDir = path.dirname(
8
- require.resolve('react-refresh/package.json')
10
+ _require.resolve('react-refresh/package.json')
9
11
  )
10
12
  const runtimeFilePath = path.join(
11
13
  reactRefreshDir,
package/src/index.ts CHANGED
@@ -1,8 +1,9 @@
1
+ import path from 'path'
1
2
  import type { ParserOptions, TransformOptions, types as t } from '@babel/core'
2
3
  import * as babel from '@babel/core'
3
4
  import { createFilter } from '@rollup/pluginutils'
4
- import resolve from 'resolve'
5
- import type { Plugin, PluginOption } from 'vite'
5
+ import { normalizePath } from 'vite'
6
+ import type { Plugin, PluginOption, ResolvedConfig } from 'vite'
6
7
  import {
7
8
  addRefreshWrapper,
8
9
  isRefreshBoundary,
@@ -38,15 +39,12 @@ export interface Options {
38
39
  * @default true
39
40
  */
40
41
  jsxPure?: boolean
41
-
42
42
  /**
43
43
  * Babel configuration applied in both dev and prod.
44
44
  */
45
- babel?: BabelOptions
46
- /**
47
- * @deprecated Use `babel.parserOpts.plugins` instead
48
- */
49
- parserPlugins?: ParserOptions['plugins']
45
+ babel?:
46
+ | BabelOptions
47
+ | ((id: string, options: { ssr?: boolean }) => BabelOptions)
50
48
  }
51
49
 
52
50
  export type BabelOptions = Omit<
@@ -72,13 +70,21 @@ export interface ReactBabelOptions extends BabelOptions {
72
70
  }
73
71
  }
74
72
 
73
+ type ReactBabelHook = (
74
+ babelConfig: ReactBabelOptions,
75
+ context: ReactBabelHookContext,
76
+ config: ResolvedConfig
77
+ ) => void
78
+
79
+ type ReactBabelHookContext = { ssr: boolean; id: string }
80
+
75
81
  declare module 'vite' {
76
82
  export interface Plugin {
77
83
  api?: {
78
84
  /**
79
85
  * Manipulate the Babel options of `@vitejs/plugin-react`
80
86
  */
81
- reactBabel?: (options: ReactBabelOptions, config: ResolvedConfig) => void
87
+ reactBabel?: ReactBabelHook
82
88
  }
83
89
  }
84
90
  }
@@ -86,26 +92,20 @@ declare module 'vite' {
86
92
  export default function viteReact(opts: Options = {}): PluginOption[] {
87
93
  // Provide default values for Rollup compat.
88
94
  let base = '/'
95
+ let resolvedCacheDir: string
89
96
  let filter = createFilter(opts.include, opts.exclude)
90
97
  let isProduction = true
91
98
  let projectRoot = process.cwd()
92
99
  let skipFastRefresh = opts.fastRefresh === false
93
100
  let skipReactImport = false
101
+ let runPluginOverrides = (
102
+ options: ReactBabelOptions,
103
+ context: ReactBabelHookContext
104
+ ) => false
105
+ let staticBabelOptions: ReactBabelOptions | undefined
94
106
 
95
107
  const useAutomaticRuntime = opts.jsxRuntime !== 'classic'
96
108
 
97
- const babelOptions = {
98
- babelrc: false,
99
- configFile: false,
100
- ...opts.babel
101
- } as ReactBabelOptions
102
-
103
- babelOptions.plugins ||= []
104
- babelOptions.presets ||= []
105
- babelOptions.overrides ||= []
106
- babelOptions.parserOpts ||= {} as any
107
- babelOptions.parserOpts.plugins ||= opts.parserPlugins || []
108
-
109
109
  // Support patterns like:
110
110
  // - import * as React from 'react';
111
111
  // - import React from 'react';
@@ -121,6 +121,7 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
121
121
  configResolved(config) {
122
122
  base = config.base
123
123
  projectRoot = config.root
124
+ resolvedCacheDir = normalizePath(path.resolve(config.cacheDir))
124
125
  filter = createFilter(opts.include, opts.exclude, {
125
126
  resolve: projectRoot
126
127
  })
@@ -146,15 +147,26 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
146
147
  `[@vitejs/plugin-react] You should stop using "${plugin.name}" ` +
147
148
  `since this plugin conflicts with it.`
148
149
  )
150
+ })
151
+
152
+ runPluginOverrides = (babelOptions, context) => {
153
+ const hooks = config.plugins
154
+ .map((plugin) => plugin.api?.reactBabel)
155
+ .filter(Boolean) as ReactBabelHook[]
149
156
 
150
- if (plugin.api?.reactBabel) {
151
- plugin.api.reactBabel(babelOptions, config)
157
+ if (hooks.length > 0) {
158
+ return (runPluginOverrides = (babelOptions) => {
159
+ hooks.forEach((hook) => hook(babelOptions, context, config))
160
+ return true
161
+ })(babelOptions)
152
162
  }
153
- })
163
+ runPluginOverrides = () => false
164
+ return false
165
+ }
154
166
  },
155
167
  async transform(code, id, options) {
156
168
  const ssr = typeof options === 'boolean' ? options : options?.ssr === true
157
- // File extension could be mocked/overriden in querystring.
169
+ // File extension could be mocked/overridden in querystring.
158
170
  const [filepath, querystring = ''] = id.split('?')
159
171
  const [extension = ''] =
160
172
  querystring.match(fileExtensionRE) ||
@@ -167,6 +179,18 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
167
179
  const isProjectFile =
168
180
  !isNodeModules && (id[0] === '\0' || id.startsWith(projectRoot + '/'))
169
181
 
182
+ let babelOptions = staticBabelOptions
183
+ if (typeof opts.babel === 'function') {
184
+ const rawOptions = opts.babel(id, { ssr })
185
+ babelOptions = createBabelOptions(rawOptions)
186
+ runPluginOverrides(babelOptions, { ssr, id: id })
187
+ } else if (!babelOptions) {
188
+ babelOptions = createBabelOptions(opts.babel)
189
+ if (!runPluginOverrides(babelOptions, { ssr, id: id })) {
190
+ staticBabelOptions = babelOptions
191
+ }
192
+ }
193
+
170
194
  const plugins = isProjectFile ? [...babelOptions.plugins] : []
171
195
 
172
196
  let useFastRefresh = false
@@ -188,8 +212,12 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
188
212
  // By reverse-compiling "React.createElement" calls into JSX,
189
213
  // React elements provided by dependencies will also use the
190
214
  // automatic runtime!
215
+ // Avoid parsing the optimized react-dom since it will never
216
+ // contain compiled JSX and it's a pretty big file (800kb).
217
+ const isOptimizedReactDom =
218
+ id.startsWith(resolvedCacheDir) && id.includes('/react-dom.js')
191
219
  const [restoredAst, isCommonJS] =
192
- !isProjectFile && !isJSX
220
+ !isProjectFile && !isJSX && !isOptimizedReactDom
193
221
  ? await restoreJSX(babel, code, id)
194
222
  : [null, false]
195
223
 
@@ -333,7 +361,7 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
333
361
  }
334
362
  }
335
363
 
336
- const runtimeId = 'react/jsx-runtime'
364
+ // const runtimeId = 'react/jsx-runtime'
337
365
  // Adapted from https://github.com/alloc/vite-react-jsx
338
366
  const viteReactJsx: Plugin = {
339
367
  name: 'vite:react-jsx',
@@ -344,10 +372,14 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
344
372
  include: ['react/jsx-dev-runtime']
345
373
  }
346
374
  }
347
- },
375
+ }
376
+ // TODO: this optimization may not be necesary and it is breacking esbuild+rollup compat,
377
+ // see https://github.com/vitejs/vite/pull/7246#discussion_r861552185
378
+ // We could still do the same trick and resolve to the optimized dependency here
379
+ /*
348
380
  resolveId(id: string) {
349
381
  return id === runtimeId ? id : null
350
- },
382
+ },
351
383
  load(id: string) {
352
384
  if (id === runtimeId) {
353
385
  const runtimePath = resolve.sync(runtimeId, {
@@ -362,7 +394,7 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
362
394
  ...exports.map((name) => `export const ${name} = jsxRuntime.${name}`)
363
395
  ].join('\n')
364
396
  }
365
- }
397
+ } */
366
398
  }
367
399
 
368
400
  return [viteBabel, viteReactRefresh, useAutomaticRuntime && viteReactJsx]
@@ -374,9 +406,18 @@ function loadPlugin(path: string): Promise<any> {
374
406
  return import(path).then((module) => module.default || module)
375
407
  }
376
408
 
377
- // overwrite for cjs require('...')() usage
378
- // The following lines are inserted by scripts/patchEsbuildDist.ts,
379
- // this doesn't bundle correctly after esbuild 0.14.4
380
- //
381
- // module.exports = viteReact
382
- // viteReact['default'] = viteReact
409
+ function createBabelOptions(rawOptions?: BabelOptions) {
410
+ const babelOptions = {
411
+ babelrc: false,
412
+ configFile: false,
413
+ ...rawOptions
414
+ } as ReactBabelOptions
415
+
416
+ babelOptions.plugins ||= []
417
+ babelOptions.presets ||= []
418
+ babelOptions.overrides ||= []
419
+ babelOptions.parserOpts ||= {} as any
420
+ babelOptions.parserOpts.plugins ||= []
421
+
422
+ return babelOptions
423
+ }