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