@vitejs/plugin-react 2.0.0-alpha.1 → 2.0.0-beta.0
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.cjs +93 -47
- package/dist/index.d.ts +9 -2
- package/dist/index.mjs +89 -42
- package/package.json +8 -10
- package/src/fast-refresh.ts +3 -3
- package/src/index.ts +115 -39
- package/src/jsx-runtime/restore-jsx.spec.ts +62 -1
- package/src/jsx-runtime/restore-jsx.ts +3 -9
package/dist/index.cjs
CHANGED
@@ -1,11 +1,10 @@
|
|
1
1
|
'use strict';
|
2
2
|
|
3
|
+
const path = require('node:path');
|
3
4
|
const babel = require('@babel/core');
|
4
|
-
const
|
5
|
-
const
|
6
|
-
const
|
7
|
-
const path = require('path');
|
8
|
-
const module$1 = require('module');
|
5
|
+
const vite = require('vite');
|
6
|
+
const fs = require('node:fs');
|
7
|
+
const node_module = require('node:module');
|
9
8
|
|
10
9
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e["default"] : e; }
|
11
10
|
|
@@ -21,13 +20,12 @@ function _interopNamespace(e) {
|
|
21
20
|
return n;
|
22
21
|
}
|
23
22
|
|
23
|
+
const path__default = /*#__PURE__*/_interopDefaultLegacy(path);
|
24
24
|
const babel__namespace = /*#__PURE__*/_interopNamespace(babel);
|
25
|
-
const resolve__default = /*#__PURE__*/_interopDefaultLegacy(resolve);
|
26
25
|
const fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
|
27
|
-
const path__default = /*#__PURE__*/_interopDefaultLegacy(path);
|
28
26
|
|
29
27
|
const runtimePublicPath = "/@react-refresh";
|
30
|
-
const _require =
|
28
|
+
const _require = node_module.createRequire((typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : (document.currentScript && document.currentScript.src || new URL('index.cjs', document.baseURI).href)));
|
31
29
|
const reactRefreshDir = path__default.dirname(_require.resolve("react-refresh/package.json"));
|
32
30
|
const runtimeFilePath = path__default.join(reactRefreshDir, "cjs/react-refresh-runtime.development.js");
|
33
31
|
const runtimeCode = `
|
@@ -140,9 +138,6 @@ async function getBabelRestoreJSX() {
|
|
140
138
|
return babelRestoreJSX;
|
141
139
|
}
|
142
140
|
async function restoreJSX(babel, code, filename) {
|
143
|
-
if (filename.includes("/.vite/react-dom.js")) {
|
144
|
-
return jsxNotFound;
|
145
|
-
}
|
146
141
|
const [reactAlias, isCommonJS] = parseReactAlias(code);
|
147
142
|
if (!reactAlias) {
|
148
143
|
return jsxNotFound;
|
@@ -177,11 +172,11 @@ async function restoreJSX(babel, code, filename) {
|
|
177
172
|
return [result?.ast, isCommonJS];
|
178
173
|
}
|
179
174
|
function parseReactAlias(code) {
|
180
|
-
let match = code.match(/\b(var|let|const)
|
175
|
+
let match = code.match(/\b(var|let|const)\s+([^=\{\s]+)\s*=\s*require\(["']react["']\)/);
|
181
176
|
if (match) {
|
182
177
|
return [match[2], true];
|
183
178
|
}
|
184
|
-
match = code.match(/^import
|
179
|
+
match = code.match(/^import\s+(?:\*\s+as\s+)?(\w+).+?\bfrom\s*["']react["']/m);
|
185
180
|
if (match) {
|
186
181
|
return [match[1], false];
|
187
182
|
}
|
@@ -189,33 +184,37 @@ function parseReactAlias(code) {
|
|
189
184
|
}
|
190
185
|
|
191
186
|
function viteReact(opts = {}) {
|
192
|
-
|
193
|
-
let
|
194
|
-
let filter =
|
187
|
+
let devBase = "/";
|
188
|
+
let resolvedCacheDir;
|
189
|
+
let filter = vite.createFilter(opts.include, opts.exclude);
|
195
190
|
let isProduction = true;
|
196
191
|
let projectRoot = process.cwd();
|
197
192
|
let skipFastRefresh = opts.fastRefresh === false;
|
198
193
|
let skipReactImport = false;
|
194
|
+
let runPluginOverrides = (options, context) => false;
|
195
|
+
let staticBabelOptions;
|
199
196
|
const useAutomaticRuntime = opts.jsxRuntime !== "classic";
|
200
|
-
const babelOptions = {
|
201
|
-
babelrc: false,
|
202
|
-
configFile: false,
|
203
|
-
...opts.babel
|
204
|
-
};
|
205
|
-
babelOptions.plugins || (babelOptions.plugins = []);
|
206
|
-
babelOptions.presets || (babelOptions.presets = []);
|
207
|
-
babelOptions.overrides || (babelOptions.overrides = []);
|
208
|
-
babelOptions.parserOpts || (babelOptions.parserOpts = {});
|
209
|
-
(_a = babelOptions.parserOpts).plugins || (_a.plugins = []);
|
210
197
|
const importReactRE = /(^|\n)import\s+(\*\s+as\s+)?React(,|\s+)/;
|
211
198
|
const fileExtensionRE = /\.[^\/\s\?]+$/;
|
212
199
|
const viteBabel = {
|
213
200
|
name: "vite:react-babel",
|
214
201
|
enforce: "pre",
|
202
|
+
config() {
|
203
|
+
if (opts.jsxRuntime === "classic") {
|
204
|
+
return {
|
205
|
+
esbuild: {
|
206
|
+
logOverride: {
|
207
|
+
"this-is-undefined-in-esm": "silent"
|
208
|
+
}
|
209
|
+
}
|
210
|
+
};
|
211
|
+
}
|
212
|
+
},
|
215
213
|
configResolved(config) {
|
216
|
-
|
214
|
+
devBase = config.base;
|
217
215
|
projectRoot = config.root;
|
218
|
-
|
216
|
+
resolvedCacheDir = vite.normalizePath(path__default.resolve(config.cacheDir));
|
217
|
+
filter = vite.createFilter(opts.include, opts.exclude, {
|
219
218
|
resolve: projectRoot
|
220
219
|
});
|
221
220
|
isProduction = config.isProduction;
|
@@ -229,19 +228,38 @@ function viteReact(opts = {}) {
|
|
229
228
|
const hasConflict = plugin.name === "react-refresh" || plugin !== viteReactJsx && plugin.name === "vite:react-jsx";
|
230
229
|
if (hasConflict)
|
231
230
|
return config.logger.warn(`[@vitejs/plugin-react] You should stop using "${plugin.name}" since this plugin conflicts with it.`);
|
232
|
-
if (plugin.api?.reactBabel) {
|
233
|
-
plugin.api.reactBabel(babelOptions, config);
|
234
|
-
}
|
235
231
|
});
|
232
|
+
runPluginOverrides = (babelOptions, context) => {
|
233
|
+
const hooks = config.plugins.map((plugin) => plugin.api?.reactBabel).filter(Boolean);
|
234
|
+
if (hooks.length > 0) {
|
235
|
+
return (runPluginOverrides = (babelOptions2) => {
|
236
|
+
hooks.forEach((hook) => hook(babelOptions2, context, config));
|
237
|
+
return true;
|
238
|
+
})(babelOptions);
|
239
|
+
}
|
240
|
+
runPluginOverrides = () => false;
|
241
|
+
return false;
|
242
|
+
};
|
236
243
|
},
|
237
244
|
async transform(code, id, options) {
|
238
|
-
const ssr =
|
245
|
+
const ssr = options?.ssr === true;
|
239
246
|
const [filepath, querystring = ""] = id.split("?");
|
240
247
|
const [extension = ""] = querystring.match(fileExtensionRE) || filepath.match(fileExtensionRE) || [];
|
241
248
|
if (/\.(mjs|[tj]sx?)$/.test(extension)) {
|
242
249
|
const isJSX = extension.endsWith("x");
|
243
250
|
const isNodeModules = id.includes("/node_modules/");
|
244
251
|
const isProjectFile = !isNodeModules && (id[0] === "\0" || id.startsWith(projectRoot + "/"));
|
252
|
+
let babelOptions = staticBabelOptions;
|
253
|
+
if (typeof opts.babel === "function") {
|
254
|
+
const rawOptions = opts.babel(id, { ssr });
|
255
|
+
babelOptions = createBabelOptions(rawOptions);
|
256
|
+
runPluginOverrides(babelOptions, { ssr, id });
|
257
|
+
} else if (!babelOptions) {
|
258
|
+
babelOptions = createBabelOptions(opts.babel);
|
259
|
+
if (!runPluginOverrides(babelOptions, { ssr, id })) {
|
260
|
+
staticBabelOptions = babelOptions;
|
261
|
+
}
|
262
|
+
}
|
245
263
|
const plugins = isProjectFile ? [...babelOptions.plugins] : [];
|
246
264
|
let useFastRefresh = false;
|
247
265
|
if (!skipFastRefresh && !ssr && !isNodeModules) {
|
@@ -257,7 +275,8 @@ function viteReact(opts = {}) {
|
|
257
275
|
let ast;
|
258
276
|
if (!isProjectFile || isJSX) {
|
259
277
|
if (useAutomaticRuntime) {
|
260
|
-
const
|
278
|
+
const isOptimizedReactDom = id.startsWith(resolvedCacheDir) && id.includes("/react-dom.js");
|
279
|
+
const [restoredAst, isCommonJS] = !isProjectFile && !isJSX && !isOptimizedReactDom ? await restoreJSX(babel__namespace, code, id) : [null, false];
|
261
280
|
if (isJSX || (ast = restoredAst)) {
|
262
281
|
plugins.push([
|
263
282
|
await loadPlugin("@babel/plugin-transform-react-jsx" + (isProduction ? "" : "-development")),
|
@@ -358,34 +377,47 @@ function viteReact(opts = {}) {
|
|
358
377
|
{
|
359
378
|
tag: "script",
|
360
379
|
attrs: { type: "module" },
|
361
|
-
children: preambleCode.replace(`__BASE__`,
|
380
|
+
children: preambleCode.replace(`__BASE__`, devBase)
|
362
381
|
}
|
363
382
|
];
|
364
383
|
}
|
365
384
|
};
|
366
|
-
const
|
385
|
+
const reactJsxRuntimeId = "react/jsx-runtime";
|
386
|
+
const reactJsxDevRuntimeId = "react/jsx-dev-runtime";
|
387
|
+
const virtualReactJsxRuntimeId = "\0" + reactJsxRuntimeId;
|
388
|
+
const virtualReactJsxDevRuntimeId = "\0" + reactJsxDevRuntimeId;
|
367
389
|
const viteReactJsx = {
|
368
390
|
name: "vite:react-jsx",
|
369
391
|
enforce: "pre",
|
370
392
|
config() {
|
371
393
|
return {
|
372
394
|
optimizeDeps: {
|
373
|
-
include: [
|
395
|
+
include: [reactJsxRuntimeId, reactJsxDevRuntimeId]
|
374
396
|
}
|
375
397
|
};
|
376
398
|
},
|
377
|
-
resolveId(id) {
|
378
|
-
|
399
|
+
resolveId(id, importer) {
|
400
|
+
if (id === reactJsxRuntimeId && importer !== virtualReactJsxRuntimeId) {
|
401
|
+
return virtualReactJsxRuntimeId;
|
402
|
+
}
|
403
|
+
if (id === reactJsxDevRuntimeId && importer !== virtualReactJsxDevRuntimeId) {
|
404
|
+
return virtualReactJsxDevRuntimeId;
|
405
|
+
}
|
379
406
|
},
|
380
407
|
load(id) {
|
381
|
-
if (id ===
|
382
|
-
const runtimePath = resolve__default.sync(runtimeId, {
|
383
|
-
basedir: projectRoot
|
384
|
-
});
|
385
|
-
const exports = ["jsx", "jsxs", "Fragment"];
|
408
|
+
if (id === virtualReactJsxRuntimeId) {
|
386
409
|
return [
|
387
|
-
`import * as jsxRuntime from ${JSON.stringify(
|
388
|
-
|
410
|
+
`import * as jsxRuntime from ${JSON.stringify(reactJsxRuntimeId)}`,
|
411
|
+
`export const Fragment = jsxRuntime.Fragment`,
|
412
|
+
`export const jsx = jsxRuntime.jsx`,
|
413
|
+
`export const jsxs = jsxRuntime.jsxs`
|
414
|
+
].join("\n");
|
415
|
+
}
|
416
|
+
if (id === virtualReactJsxDevRuntimeId) {
|
417
|
+
return [
|
418
|
+
`import * as jsxRuntime from ${JSON.stringify(reactJsxDevRuntimeId)}`,
|
419
|
+
`export const Fragment = jsxRuntime.Fragment`,
|
420
|
+
`export const jsxDEV = jsxRuntime.jsxDEV`
|
389
421
|
].join("\n");
|
390
422
|
}
|
391
423
|
}
|
@@ -393,8 +425,22 @@ function viteReact(opts = {}) {
|
|
393
425
|
return [viteBabel, viteReactRefresh, useAutomaticRuntime && viteReactJsx];
|
394
426
|
}
|
395
427
|
viteReact.preambleCode = preambleCode;
|
396
|
-
function loadPlugin(
|
397
|
-
return import(
|
428
|
+
function loadPlugin(path2) {
|
429
|
+
return import(path2).then((module) => module.default || module);
|
430
|
+
}
|
431
|
+
function createBabelOptions(rawOptions) {
|
432
|
+
var _a;
|
433
|
+
const babelOptions = {
|
434
|
+
babelrc: false,
|
435
|
+
configFile: false,
|
436
|
+
...rawOptions
|
437
|
+
};
|
438
|
+
babelOptions.plugins || (babelOptions.plugins = []);
|
439
|
+
babelOptions.presets || (babelOptions.presets = []);
|
440
|
+
babelOptions.overrides || (babelOptions.overrides = []);
|
441
|
+
babelOptions.parserOpts || (babelOptions.parserOpts = {});
|
442
|
+
(_a = babelOptions.parserOpts).plugins || (_a.plugins = []);
|
443
|
+
return babelOptions;
|
398
444
|
}
|
399
445
|
|
400
446
|
module.exports = viteReact;
|
package/dist/index.d.ts
CHANGED
@@ -29,7 +29,9 @@ interface Options {
|
|
29
29
|
/**
|
30
30
|
* Babel configuration applied in both dev and prod.
|
31
31
|
*/
|
32
|
-
babel?: BabelOptions
|
32
|
+
babel?: BabelOptions | ((id: string, options: {
|
33
|
+
ssr?: boolean;
|
34
|
+
}) => BabelOptions);
|
33
35
|
}
|
34
36
|
declare type BabelOptions = Omit<TransformOptions, 'ast' | 'filename' | 'root' | 'sourceFileName' | 'sourceMaps' | 'inputSourceMap'>;
|
35
37
|
/**
|
@@ -44,13 +46,18 @@ interface ReactBabelOptions extends BabelOptions {
|
|
44
46
|
plugins: Extract<ParserOptions['plugins'], any[]>;
|
45
47
|
};
|
46
48
|
}
|
49
|
+
declare type ReactBabelHook = (babelConfig: ReactBabelOptions, context: ReactBabelHookContext, config: ResolvedConfig) => void;
|
50
|
+
declare type ReactBabelHookContext = {
|
51
|
+
ssr: boolean;
|
52
|
+
id: string;
|
53
|
+
};
|
47
54
|
declare module 'vite' {
|
48
55
|
interface Plugin {
|
49
56
|
api?: {
|
50
57
|
/**
|
51
58
|
* Manipulate the Babel options of `@vitejs/plugin-react`
|
52
59
|
*/
|
53
|
-
reactBabel?:
|
60
|
+
reactBabel?: ReactBabelHook;
|
54
61
|
};
|
55
62
|
}
|
56
63
|
}
|
package/dist/index.mjs
CHANGED
@@ -1,9 +1,8 @@
|
|
1
|
+
import path from 'node:path';
|
1
2
|
import * as babel from '@babel/core';
|
2
|
-
import { createFilter } from '
|
3
|
-
import
|
4
|
-
import
|
5
|
-
import path from 'path';
|
6
|
-
import { createRequire } from 'module';
|
3
|
+
import { createFilter, normalizePath } from 'vite';
|
4
|
+
import fs from 'node:fs';
|
5
|
+
import { createRequire } from 'node:module';
|
7
6
|
|
8
7
|
const runtimePublicPath = "/@react-refresh";
|
9
8
|
const _require = createRequire(import.meta.url);
|
@@ -119,9 +118,6 @@ async function getBabelRestoreJSX() {
|
|
119
118
|
return babelRestoreJSX;
|
120
119
|
}
|
121
120
|
async function restoreJSX(babel, code, filename) {
|
122
|
-
if (filename.includes("/.vite/react-dom.js")) {
|
123
|
-
return jsxNotFound;
|
124
|
-
}
|
125
121
|
const [reactAlias, isCommonJS] = parseReactAlias(code);
|
126
122
|
if (!reactAlias) {
|
127
123
|
return jsxNotFound;
|
@@ -156,11 +152,11 @@ async function restoreJSX(babel, code, filename) {
|
|
156
152
|
return [result?.ast, isCommonJS];
|
157
153
|
}
|
158
154
|
function parseReactAlias(code) {
|
159
|
-
let match = code.match(/\b(var|let|const)
|
155
|
+
let match = code.match(/\b(var|let|const)\s+([^=\{\s]+)\s*=\s*require\(["']react["']\)/);
|
160
156
|
if (match) {
|
161
157
|
return [match[2], true];
|
162
158
|
}
|
163
|
-
match = code.match(/^import
|
159
|
+
match = code.match(/^import\s+(?:\*\s+as\s+)?(\w+).+?\bfrom\s*["']react["']/m);
|
164
160
|
if (match) {
|
165
161
|
return [match[1], false];
|
166
162
|
}
|
@@ -168,32 +164,36 @@ function parseReactAlias(code) {
|
|
168
164
|
}
|
169
165
|
|
170
166
|
function viteReact(opts = {}) {
|
171
|
-
|
172
|
-
let
|
167
|
+
let devBase = "/";
|
168
|
+
let resolvedCacheDir;
|
173
169
|
let filter = createFilter(opts.include, opts.exclude);
|
174
170
|
let isProduction = true;
|
175
171
|
let projectRoot = process.cwd();
|
176
172
|
let skipFastRefresh = opts.fastRefresh === false;
|
177
173
|
let skipReactImport = false;
|
174
|
+
let runPluginOverrides = (options, context) => false;
|
175
|
+
let staticBabelOptions;
|
178
176
|
const useAutomaticRuntime = opts.jsxRuntime !== "classic";
|
179
|
-
const babelOptions = {
|
180
|
-
babelrc: false,
|
181
|
-
configFile: false,
|
182
|
-
...opts.babel
|
183
|
-
};
|
184
|
-
babelOptions.plugins || (babelOptions.plugins = []);
|
185
|
-
babelOptions.presets || (babelOptions.presets = []);
|
186
|
-
babelOptions.overrides || (babelOptions.overrides = []);
|
187
|
-
babelOptions.parserOpts || (babelOptions.parserOpts = {});
|
188
|
-
(_a = babelOptions.parserOpts).plugins || (_a.plugins = []);
|
189
177
|
const importReactRE = /(^|\n)import\s+(\*\s+as\s+)?React(,|\s+)/;
|
190
178
|
const fileExtensionRE = /\.[^\/\s\?]+$/;
|
191
179
|
const viteBabel = {
|
192
180
|
name: "vite:react-babel",
|
193
181
|
enforce: "pre",
|
182
|
+
config() {
|
183
|
+
if (opts.jsxRuntime === "classic") {
|
184
|
+
return {
|
185
|
+
esbuild: {
|
186
|
+
logOverride: {
|
187
|
+
"this-is-undefined-in-esm": "silent"
|
188
|
+
}
|
189
|
+
}
|
190
|
+
};
|
191
|
+
}
|
192
|
+
},
|
194
193
|
configResolved(config) {
|
195
|
-
|
194
|
+
devBase = config.base;
|
196
195
|
projectRoot = config.root;
|
196
|
+
resolvedCacheDir = normalizePath(path.resolve(config.cacheDir));
|
197
197
|
filter = createFilter(opts.include, opts.exclude, {
|
198
198
|
resolve: projectRoot
|
199
199
|
});
|
@@ -208,19 +208,38 @@ function viteReact(opts = {}) {
|
|
208
208
|
const hasConflict = plugin.name === "react-refresh" || plugin !== viteReactJsx && plugin.name === "vite:react-jsx";
|
209
209
|
if (hasConflict)
|
210
210
|
return config.logger.warn(`[@vitejs/plugin-react] You should stop using "${plugin.name}" since this plugin conflicts with it.`);
|
211
|
-
if (plugin.api?.reactBabel) {
|
212
|
-
plugin.api.reactBabel(babelOptions, config);
|
213
|
-
}
|
214
211
|
});
|
212
|
+
runPluginOverrides = (babelOptions, context) => {
|
213
|
+
const hooks = config.plugins.map((plugin) => plugin.api?.reactBabel).filter(Boolean);
|
214
|
+
if (hooks.length > 0) {
|
215
|
+
return (runPluginOverrides = (babelOptions2) => {
|
216
|
+
hooks.forEach((hook) => hook(babelOptions2, context, config));
|
217
|
+
return true;
|
218
|
+
})(babelOptions);
|
219
|
+
}
|
220
|
+
runPluginOverrides = () => false;
|
221
|
+
return false;
|
222
|
+
};
|
215
223
|
},
|
216
224
|
async transform(code, id, options) {
|
217
|
-
const ssr =
|
225
|
+
const ssr = options?.ssr === true;
|
218
226
|
const [filepath, querystring = ""] = id.split("?");
|
219
227
|
const [extension = ""] = querystring.match(fileExtensionRE) || filepath.match(fileExtensionRE) || [];
|
220
228
|
if (/\.(mjs|[tj]sx?)$/.test(extension)) {
|
221
229
|
const isJSX = extension.endsWith("x");
|
222
230
|
const isNodeModules = id.includes("/node_modules/");
|
223
231
|
const isProjectFile = !isNodeModules && (id[0] === "\0" || id.startsWith(projectRoot + "/"));
|
232
|
+
let babelOptions = staticBabelOptions;
|
233
|
+
if (typeof opts.babel === "function") {
|
234
|
+
const rawOptions = opts.babel(id, { ssr });
|
235
|
+
babelOptions = createBabelOptions(rawOptions);
|
236
|
+
runPluginOverrides(babelOptions, { ssr, id });
|
237
|
+
} else if (!babelOptions) {
|
238
|
+
babelOptions = createBabelOptions(opts.babel);
|
239
|
+
if (!runPluginOverrides(babelOptions, { ssr, id })) {
|
240
|
+
staticBabelOptions = babelOptions;
|
241
|
+
}
|
242
|
+
}
|
224
243
|
const plugins = isProjectFile ? [...babelOptions.plugins] : [];
|
225
244
|
let useFastRefresh = false;
|
226
245
|
if (!skipFastRefresh && !ssr && !isNodeModules) {
|
@@ -236,7 +255,8 @@ function viteReact(opts = {}) {
|
|
236
255
|
let ast;
|
237
256
|
if (!isProjectFile || isJSX) {
|
238
257
|
if (useAutomaticRuntime) {
|
239
|
-
const
|
258
|
+
const isOptimizedReactDom = id.startsWith(resolvedCacheDir) && id.includes("/react-dom.js");
|
259
|
+
const [restoredAst, isCommonJS] = !isProjectFile && !isJSX && !isOptimizedReactDom ? await restoreJSX(babel, code, id) : [null, false];
|
240
260
|
if (isJSX || (ast = restoredAst)) {
|
241
261
|
plugins.push([
|
242
262
|
await loadPlugin("@babel/plugin-transform-react-jsx" + (isProduction ? "" : "-development")),
|
@@ -337,34 +357,47 @@ function viteReact(opts = {}) {
|
|
337
357
|
{
|
338
358
|
tag: "script",
|
339
359
|
attrs: { type: "module" },
|
340
|
-
children: preambleCode.replace(`__BASE__`,
|
360
|
+
children: preambleCode.replace(`__BASE__`, devBase)
|
341
361
|
}
|
342
362
|
];
|
343
363
|
}
|
344
364
|
};
|
345
|
-
const
|
365
|
+
const reactJsxRuntimeId = "react/jsx-runtime";
|
366
|
+
const reactJsxDevRuntimeId = "react/jsx-dev-runtime";
|
367
|
+
const virtualReactJsxRuntimeId = "\0" + reactJsxRuntimeId;
|
368
|
+
const virtualReactJsxDevRuntimeId = "\0" + reactJsxDevRuntimeId;
|
346
369
|
const viteReactJsx = {
|
347
370
|
name: "vite:react-jsx",
|
348
371
|
enforce: "pre",
|
349
372
|
config() {
|
350
373
|
return {
|
351
374
|
optimizeDeps: {
|
352
|
-
include: [
|
375
|
+
include: [reactJsxRuntimeId, reactJsxDevRuntimeId]
|
353
376
|
}
|
354
377
|
};
|
355
378
|
},
|
356
|
-
resolveId(id) {
|
357
|
-
|
379
|
+
resolveId(id, importer) {
|
380
|
+
if (id === reactJsxRuntimeId && importer !== virtualReactJsxRuntimeId) {
|
381
|
+
return virtualReactJsxRuntimeId;
|
382
|
+
}
|
383
|
+
if (id === reactJsxDevRuntimeId && importer !== virtualReactJsxDevRuntimeId) {
|
384
|
+
return virtualReactJsxDevRuntimeId;
|
385
|
+
}
|
358
386
|
},
|
359
387
|
load(id) {
|
360
|
-
if (id ===
|
361
|
-
const runtimePath = resolve.sync(runtimeId, {
|
362
|
-
basedir: projectRoot
|
363
|
-
});
|
364
|
-
const exports = ["jsx", "jsxs", "Fragment"];
|
388
|
+
if (id === virtualReactJsxRuntimeId) {
|
365
389
|
return [
|
366
|
-
`import * as jsxRuntime from ${JSON.stringify(
|
367
|
-
|
390
|
+
`import * as jsxRuntime from ${JSON.stringify(reactJsxRuntimeId)}`,
|
391
|
+
`export const Fragment = jsxRuntime.Fragment`,
|
392
|
+
`export const jsx = jsxRuntime.jsx`,
|
393
|
+
`export const jsxs = jsxRuntime.jsxs`
|
394
|
+
].join("\n");
|
395
|
+
}
|
396
|
+
if (id === virtualReactJsxDevRuntimeId) {
|
397
|
+
return [
|
398
|
+
`import * as jsxRuntime from ${JSON.stringify(reactJsxDevRuntimeId)}`,
|
399
|
+
`export const Fragment = jsxRuntime.Fragment`,
|
400
|
+
`export const jsxDEV = jsxRuntime.jsxDEV`
|
368
401
|
].join("\n");
|
369
402
|
}
|
370
403
|
}
|
@@ -372,8 +405,22 @@ function viteReact(opts = {}) {
|
|
372
405
|
return [viteBabel, viteReactRefresh, useAutomaticRuntime && viteReactJsx];
|
373
406
|
}
|
374
407
|
viteReact.preambleCode = preambleCode;
|
375
|
-
function loadPlugin(
|
376
|
-
return import(
|
408
|
+
function loadPlugin(path2) {
|
409
|
+
return import(path2).then((module) => module.default || module);
|
410
|
+
}
|
411
|
+
function createBabelOptions(rawOptions) {
|
412
|
+
var _a;
|
413
|
+
const babelOptions = {
|
414
|
+
babelrc: false,
|
415
|
+
configFile: false,
|
416
|
+
...rawOptions
|
417
|
+
};
|
418
|
+
babelOptions.plugins || (babelOptions.plugins = []);
|
419
|
+
babelOptions.presets || (babelOptions.presets = []);
|
420
|
+
babelOptions.overrides || (babelOptions.overrides = []);
|
421
|
+
babelOptions.parserOpts || (babelOptions.parserOpts = {});
|
422
|
+
(_a = babelOptions.parserOpts).plugins || (_a.plugins = []);
|
423
|
+
return babelOptions;
|
377
424
|
}
|
378
425
|
|
379
426
|
export { viteReact as default };
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@vitejs/plugin-react",
|
3
|
-
"version": "2.0.0-
|
3
|
+
"version": "2.0.0-beta.0",
|
4
4
|
"license": "MIT",
|
5
5
|
"author": "Evan You",
|
6
6
|
"contributors": [
|
@@ -23,11 +23,11 @@
|
|
23
23
|
"scripts": {
|
24
24
|
"dev": "unbuild --stub",
|
25
25
|
"build": "unbuild && pnpm run patch-cjs",
|
26
|
-
"patch-cjs": "
|
26
|
+
"patch-cjs": "esno ../../scripts/patchCJS.ts",
|
27
27
|
"prepublishOnly": "npm run build"
|
28
28
|
},
|
29
29
|
"engines": {
|
30
|
-
"node": ">=14.
|
30
|
+
"node": ">=14.18.0"
|
31
31
|
},
|
32
32
|
"repository": {
|
33
33
|
"type": "git",
|
@@ -39,17 +39,15 @@
|
|
39
39
|
},
|
40
40
|
"homepage": "https://github.com/vitejs/vite/tree/main/packages/plugin-react#readme",
|
41
41
|
"dependencies": {
|
42
|
-
"@babel/core": "^7.
|
43
|
-
"@babel/plugin-transform-react-jsx": "^7.17.
|
42
|
+
"@babel/core": "^7.18.5",
|
43
|
+
"@babel/plugin-transform-react-jsx": "^7.17.12",
|
44
44
|
"@babel/plugin-transform-react-jsx-development": "^7.16.7",
|
45
|
-
"@babel/plugin-transform-react-jsx-self": "^7.
|
45
|
+
"@babel/plugin-transform-react-jsx-self": "^7.17.12",
|
46
46
|
"@babel/plugin-transform-react-jsx-source": "^7.16.7",
|
47
|
-
"
|
48
|
-
"react-refresh": "^0.13.0",
|
49
|
-
"resolve": "^1.22.0"
|
47
|
+
"react-refresh": "^0.14.0"
|
50
48
|
},
|
51
49
|
"peerDependencies": {
|
52
|
-
"vite": "^3.0.0-alpha"
|
50
|
+
"vite": "^3.0.0-alpha.11"
|
53
51
|
},
|
54
52
|
"devDependencies": {
|
55
53
|
"vite": "workspace:*"
|
package/src/fast-refresh.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
import fs from 'fs'
|
2
|
-
import path from 'path'
|
3
|
-
import { createRequire } from 'module'
|
1
|
+
import fs from 'node:fs'
|
2
|
+
import path from 'node:path'
|
3
|
+
import { createRequire } from 'node:module'
|
4
4
|
import type { types as t } from '@babel/core'
|
5
5
|
|
6
6
|
export const runtimePublicPath = '/@react-refresh'
|
package/src/index.ts
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
+
import path from 'node:path'
|
1
2
|
import type { ParserOptions, TransformOptions, types as t } from '@babel/core'
|
2
3
|
import * as babel from '@babel/core'
|
3
|
-
import { createFilter } from '
|
4
|
-
import resolve from 'resolve'
|
4
|
+
import { createFilter, normalizePath } from 'vite'
|
5
5
|
import type { Plugin, PluginOption, ResolvedConfig } from 'vite'
|
6
6
|
import {
|
7
7
|
addRefreshWrapper,
|
@@ -41,7 +41,9 @@ export interface Options {
|
|
41
41
|
/**
|
42
42
|
* Babel configuration applied in both dev and prod.
|
43
43
|
*/
|
44
|
-
babel?:
|
44
|
+
babel?:
|
45
|
+
| BabelOptions
|
46
|
+
| ((id: string, options: { ssr?: boolean }) => BabelOptions)
|
45
47
|
}
|
46
48
|
|
47
49
|
export type BabelOptions = Omit<
|
@@ -67,40 +69,42 @@ export interface ReactBabelOptions extends BabelOptions {
|
|
67
69
|
}
|
68
70
|
}
|
69
71
|
|
72
|
+
type ReactBabelHook = (
|
73
|
+
babelConfig: ReactBabelOptions,
|
74
|
+
context: ReactBabelHookContext,
|
75
|
+
config: ResolvedConfig
|
76
|
+
) => void
|
77
|
+
|
78
|
+
type ReactBabelHookContext = { ssr: boolean; id: string }
|
79
|
+
|
70
80
|
declare module 'vite' {
|
71
81
|
export interface Plugin {
|
72
82
|
api?: {
|
73
83
|
/**
|
74
84
|
* Manipulate the Babel options of `@vitejs/plugin-react`
|
75
85
|
*/
|
76
|
-
reactBabel?:
|
86
|
+
reactBabel?: ReactBabelHook
|
77
87
|
}
|
78
88
|
}
|
79
89
|
}
|
80
90
|
|
81
91
|
export default function viteReact(opts: Options = {}): PluginOption[] {
|
82
92
|
// Provide default values for Rollup compat.
|
83
|
-
let
|
93
|
+
let devBase = '/'
|
94
|
+
let resolvedCacheDir: string
|
84
95
|
let filter = createFilter(opts.include, opts.exclude)
|
85
96
|
let isProduction = true
|
86
97
|
let projectRoot = process.cwd()
|
87
98
|
let skipFastRefresh = opts.fastRefresh === false
|
88
99
|
let skipReactImport = false
|
100
|
+
let runPluginOverrides = (
|
101
|
+
options: ReactBabelOptions,
|
102
|
+
context: ReactBabelHookContext
|
103
|
+
) => false
|
104
|
+
let staticBabelOptions: ReactBabelOptions | undefined
|
89
105
|
|
90
106
|
const useAutomaticRuntime = opts.jsxRuntime !== 'classic'
|
91
107
|
|
92
|
-
const babelOptions = {
|
93
|
-
babelrc: false,
|
94
|
-
configFile: false,
|
95
|
-
...opts.babel
|
96
|
-
} as ReactBabelOptions
|
97
|
-
|
98
|
-
babelOptions.plugins ||= []
|
99
|
-
babelOptions.presets ||= []
|
100
|
-
babelOptions.overrides ||= []
|
101
|
-
babelOptions.parserOpts ||= {} as any
|
102
|
-
babelOptions.parserOpts.plugins ||= []
|
103
|
-
|
104
108
|
// Support patterns like:
|
105
109
|
// - import * as React from 'react';
|
106
110
|
// - import React from 'react';
|
@@ -113,9 +117,21 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
|
|
113
117
|
const viteBabel: Plugin = {
|
114
118
|
name: 'vite:react-babel',
|
115
119
|
enforce: 'pre',
|
120
|
+
config() {
|
121
|
+
if (opts.jsxRuntime === 'classic') {
|
122
|
+
return {
|
123
|
+
esbuild: {
|
124
|
+
logOverride: {
|
125
|
+
'this-is-undefined-in-esm': 'silent'
|
126
|
+
}
|
127
|
+
}
|
128
|
+
}
|
129
|
+
}
|
130
|
+
},
|
116
131
|
configResolved(config) {
|
117
|
-
|
132
|
+
devBase = config.base
|
118
133
|
projectRoot = config.root
|
134
|
+
resolvedCacheDir = normalizePath(path.resolve(config.cacheDir))
|
119
135
|
filter = createFilter(opts.include, opts.exclude, {
|
120
136
|
resolve: projectRoot
|
121
137
|
})
|
@@ -141,14 +157,25 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
|
|
141
157
|
`[@vitejs/plugin-react] You should stop using "${plugin.name}" ` +
|
142
158
|
`since this plugin conflicts with it.`
|
143
159
|
)
|
160
|
+
})
|
161
|
+
|
162
|
+
runPluginOverrides = (babelOptions, context) => {
|
163
|
+
const hooks = config.plugins
|
164
|
+
.map((plugin) => plugin.api?.reactBabel)
|
165
|
+
.filter(Boolean) as ReactBabelHook[]
|
144
166
|
|
145
|
-
if (
|
146
|
-
|
167
|
+
if (hooks.length > 0) {
|
168
|
+
return (runPluginOverrides = (babelOptions) => {
|
169
|
+
hooks.forEach((hook) => hook(babelOptions, context, config))
|
170
|
+
return true
|
171
|
+
})(babelOptions)
|
147
172
|
}
|
148
|
-
|
173
|
+
runPluginOverrides = () => false
|
174
|
+
return false
|
175
|
+
}
|
149
176
|
},
|
150
177
|
async transform(code, id, options) {
|
151
|
-
const ssr =
|
178
|
+
const ssr = options?.ssr === true
|
152
179
|
// File extension could be mocked/overridden in querystring.
|
153
180
|
const [filepath, querystring = ''] = id.split('?')
|
154
181
|
const [extension = ''] =
|
@@ -162,6 +189,18 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
|
|
162
189
|
const isProjectFile =
|
163
190
|
!isNodeModules && (id[0] === '\0' || id.startsWith(projectRoot + '/'))
|
164
191
|
|
192
|
+
let babelOptions = staticBabelOptions
|
193
|
+
if (typeof opts.babel === 'function') {
|
194
|
+
const rawOptions = opts.babel(id, { ssr })
|
195
|
+
babelOptions = createBabelOptions(rawOptions)
|
196
|
+
runPluginOverrides(babelOptions, { ssr, id: id })
|
197
|
+
} else if (!babelOptions) {
|
198
|
+
babelOptions = createBabelOptions(opts.babel)
|
199
|
+
if (!runPluginOverrides(babelOptions, { ssr, id: id })) {
|
200
|
+
staticBabelOptions = babelOptions
|
201
|
+
}
|
202
|
+
}
|
203
|
+
|
165
204
|
const plugins = isProjectFile ? [...babelOptions.plugins] : []
|
166
205
|
|
167
206
|
let useFastRefresh = false
|
@@ -183,8 +222,12 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
|
|
183
222
|
// By reverse-compiling "React.createElement" calls into JSX,
|
184
223
|
// React elements provided by dependencies will also use the
|
185
224
|
// automatic runtime!
|
225
|
+
// Avoid parsing the optimized react-dom since it will never
|
226
|
+
// contain compiled JSX and it's a pretty big file (800kb).
|
227
|
+
const isOptimizedReactDom =
|
228
|
+
id.startsWith(resolvedCacheDir) && id.includes('/react-dom.js')
|
186
229
|
const [restoredAst, isCommonJS] =
|
187
|
-
!isProjectFile && !isJSX
|
230
|
+
!isProjectFile && !isJSX && !isOptimizedReactDom
|
188
231
|
? await restoreJSX(babel, code, id)
|
189
232
|
: [null, false]
|
190
233
|
|
@@ -322,13 +365,16 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
|
|
322
365
|
{
|
323
366
|
tag: 'script',
|
324
367
|
attrs: { type: 'module' },
|
325
|
-
children: preambleCode.replace(`__BASE__`,
|
368
|
+
children: preambleCode.replace(`__BASE__`, devBase)
|
326
369
|
}
|
327
370
|
]
|
328
371
|
}
|
329
372
|
}
|
330
373
|
|
331
|
-
const
|
374
|
+
const reactJsxRuntimeId = 'react/jsx-runtime'
|
375
|
+
const reactJsxDevRuntimeId = 'react/jsx-dev-runtime'
|
376
|
+
const virtualReactJsxRuntimeId = '\0' + reactJsxRuntimeId
|
377
|
+
const virtualReactJsxDevRuntimeId = '\0' + reactJsxDevRuntimeId
|
332
378
|
// Adapted from https://github.com/alloc/vite-react-jsx
|
333
379
|
const viteReactJsx: Plugin = {
|
334
380
|
name: 'vite:react-jsx',
|
@@ -336,25 +382,39 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
|
|
336
382
|
config() {
|
337
383
|
return {
|
338
384
|
optimizeDeps: {
|
339
|
-
include: [
|
385
|
+
include: [reactJsxRuntimeId, reactJsxDevRuntimeId]
|
340
386
|
}
|
341
387
|
}
|
342
388
|
},
|
343
|
-
resolveId(id
|
344
|
-
|
389
|
+
resolveId(id, importer) {
|
390
|
+
// Resolve runtime to a virtual path to be interoped.
|
391
|
+
// Since the interop code re-imports `id`, we need to prevent re-resolving
|
392
|
+
// to the virtual id if the importer is already the virtual id.
|
393
|
+
if (id === reactJsxRuntimeId && importer !== virtualReactJsxRuntimeId) {
|
394
|
+
return virtualReactJsxRuntimeId
|
395
|
+
}
|
396
|
+
if (
|
397
|
+
id === reactJsxDevRuntimeId &&
|
398
|
+
importer !== virtualReactJsxDevRuntimeId
|
399
|
+
) {
|
400
|
+
return virtualReactJsxDevRuntimeId
|
401
|
+
}
|
345
402
|
},
|
346
|
-
load(id
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
403
|
+
load(id) {
|
404
|
+
// Apply manual interop
|
405
|
+
if (id === virtualReactJsxRuntimeId) {
|
406
|
+
return [
|
407
|
+
`import * as jsxRuntime from ${JSON.stringify(reactJsxRuntimeId)}`,
|
408
|
+
`export const Fragment = jsxRuntime.Fragment`,
|
409
|
+
`export const jsx = jsxRuntime.jsx`,
|
410
|
+
`export const jsxs = jsxRuntime.jsxs`
|
411
|
+
].join('\n')
|
412
|
+
}
|
413
|
+
if (id === virtualReactJsxDevRuntimeId) {
|
352
414
|
return [
|
353
|
-
`import * as jsxRuntime from ${JSON.stringify(
|
354
|
-
|
355
|
-
|
356
|
-
// instead of the more concise `jsx` alias.
|
357
|
-
...exports.map((name) => `export const ${name} = jsxRuntime.${name}`)
|
415
|
+
`import * as jsxRuntime from ${JSON.stringify(reactJsxDevRuntimeId)}`,
|
416
|
+
`export const Fragment = jsxRuntime.Fragment`,
|
417
|
+
`export const jsxDEV = jsxRuntime.jsxDEV`
|
358
418
|
].join('\n')
|
359
419
|
}
|
360
420
|
}
|
@@ -368,3 +428,19 @@ viteReact.preambleCode = preambleCode
|
|
368
428
|
function loadPlugin(path: string): Promise<any> {
|
369
429
|
return import(path).then((module) => module.default || module)
|
370
430
|
}
|
431
|
+
|
432
|
+
function createBabelOptions(rawOptions?: BabelOptions) {
|
433
|
+
const babelOptions = {
|
434
|
+
babelrc: false,
|
435
|
+
configFile: false,
|
436
|
+
...rawOptions
|
437
|
+
} as ReactBabelOptions
|
438
|
+
|
439
|
+
babelOptions.plugins ||= []
|
440
|
+
babelOptions.presets ||= []
|
441
|
+
babelOptions.overrides ||= []
|
442
|
+
babelOptions.parserOpts ||= {} as any
|
443
|
+
babelOptions.parserOpts.plugins ||= []
|
444
|
+
|
445
|
+
return babelOptions
|
446
|
+
}
|
@@ -1,6 +1,67 @@
|
|
1
1
|
import * as babel from '@babel/core'
|
2
2
|
import { describe, expect, it } from 'vitest'
|
3
|
-
import { restoreJSX } from './restore-jsx'
|
3
|
+
import { parseReactAlias, restoreJSX } from './restore-jsx'
|
4
|
+
|
5
|
+
describe('parseReactAlias', () => {
|
6
|
+
it('handles cjs require', () => {
|
7
|
+
expect(parseReactAlias(`const React = require("react")`))
|
8
|
+
.toMatchInlineSnapshot(`
|
9
|
+
[
|
10
|
+
"React",
|
11
|
+
true,
|
12
|
+
]
|
13
|
+
`)
|
14
|
+
})
|
15
|
+
|
16
|
+
it('handles cjs require (minified)', () => {
|
17
|
+
expect(parseReactAlias(`var F=require('foo');var R=require('react')`))
|
18
|
+
.toMatchInlineSnapshot(`
|
19
|
+
[
|
20
|
+
"R",
|
21
|
+
true,
|
22
|
+
]
|
23
|
+
`)
|
24
|
+
})
|
25
|
+
|
26
|
+
it('does not handle destructured cjs require', () => {
|
27
|
+
expect(parseReactAlias(`var {createElement} = require("react")`))
|
28
|
+
.toMatchInlineSnapshot(`
|
29
|
+
[
|
30
|
+
undefined,
|
31
|
+
false,
|
32
|
+
]
|
33
|
+
`)
|
34
|
+
})
|
35
|
+
|
36
|
+
it('handles esm import', () => {
|
37
|
+
expect(parseReactAlias(`import React from 'react'`)).toMatchInlineSnapshot(`
|
38
|
+
[
|
39
|
+
"React",
|
40
|
+
false,
|
41
|
+
]
|
42
|
+
`)
|
43
|
+
})
|
44
|
+
|
45
|
+
it('handles esm import namespace', () => {
|
46
|
+
expect(parseReactAlias(`import * as React from "react"`))
|
47
|
+
.toMatchInlineSnapshot(`
|
48
|
+
[
|
49
|
+
"React",
|
50
|
+
false,
|
51
|
+
]
|
52
|
+
`)
|
53
|
+
})
|
54
|
+
|
55
|
+
it('does not handle destructured esm import', () => {
|
56
|
+
expect(parseReactAlias(`import {createElement} from "react"`))
|
57
|
+
.toMatchInlineSnapshot(`
|
58
|
+
[
|
59
|
+
undefined,
|
60
|
+
false,
|
61
|
+
]
|
62
|
+
`)
|
63
|
+
})
|
64
|
+
})
|
4
65
|
|
5
66
|
async function jsx(sourceCode: string) {
|
6
67
|
const [ast] = await restoreJSX(babel, sourceCode, 'test.js')
|
@@ -27,12 +27,6 @@ export async function restoreJSX(
|
|
27
27
|
code: string,
|
28
28
|
filename: string
|
29
29
|
): Promise<RestoredJSX> {
|
30
|
-
// Avoid parsing the optimized react-dom since it will never
|
31
|
-
// contain compiled JSX and it's a pretty big file (800kb).
|
32
|
-
if (filename.includes('/.vite/react-dom.js')) {
|
33
|
-
return jsxNotFound
|
34
|
-
}
|
35
|
-
|
36
30
|
const [reactAlias, isCommonJS] = parseReactAlias(code)
|
37
31
|
|
38
32
|
if (!reactAlias) {
|
@@ -85,16 +79,16 @@ export async function restoreJSX(
|
|
85
79
|
return [result?.ast, isCommonJS]
|
86
80
|
}
|
87
81
|
|
88
|
-
function parseReactAlias(
|
82
|
+
export function parseReactAlias(
|
89
83
|
code: string
|
90
84
|
): [alias: string | undefined, isCommonJS: boolean] {
|
91
85
|
let match = code.match(
|
92
|
-
/\b(var|let|const)
|
86
|
+
/\b(var|let|const)\s+([^=\{\s]+)\s*=\s*require\(["']react["']\)/
|
93
87
|
)
|
94
88
|
if (match) {
|
95
89
|
return [match[2], true]
|
96
90
|
}
|
97
|
-
match = code.match(/^import
|
91
|
+
match = code.match(/^import\s+(?:\*\s+as\s+)?(\w+).+?\bfrom\s*["']react["']/m)
|
98
92
|
if (match) {
|
99
93
|
return [match[1], false]
|
100
94
|
}
|