@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/chunks/babel-restore-jsx.cjs +125 -0
- package/dist/chunks/babel-restore-jsx.mjs +123 -0
- package/dist/{index.js → index.cjs} +108 -248
- package/dist/index.d.ts +69 -62
- package/dist/index.mjs +387 -0
- package/package.json +25 -13
- package/src/fast-refresh.ts +4 -2
- package/src/index.ts +77 -36
- package/src/jsx-runtime/babel-import-to-require.ts +2 -3
- package/src/jsx-runtime/babel-restore-jsx.spec.ts +2 -1
- package/src/jsx-runtime/restore-jsx.spec.ts +3 -2
- package/src/jsx-runtime/restore-jsx.ts +18 -13
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": "
|
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.
|
14
|
-
"
|
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": "
|
17
|
-
"build": "
|
18
|
-
"
|
19
|
-
"
|
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": ">=
|
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.
|
37
|
-
"@babel/plugin-transform-react-jsx": "^7.17.
|
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.
|
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
|
}
|
package/src/fast-refresh.ts
CHANGED
@@ -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
|
-
|
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
|
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?:
|
46
|
-
|
47
|
-
|
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?:
|
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 (
|
151
|
-
|
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/
|
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
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
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
|
+
}
|