@vitejs/plugin-react 2.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/chunks/babel-restore-jsx.cjs +17 -6
- package/dist/chunks/babel-restore-jsx.mjs +17 -6
- package/dist/index.cjs +45 -26
- package/dist/index.mjs +45 -26
- package/package.json +4 -4
- package/src/fast-refresh.ts +1 -0
- package/src/jsx-runtime/babel-restore-jsx.ts +10 -3
- package/src/jsx-runtime/restore-jsx.spec.ts +32 -2
- package/src/jsx-runtime/restore-jsx.ts +6 -28
package/README.md
CHANGED
@@ -83,7 +83,7 @@ Here's the [complete list of Babel parser plugins](https://babeljs.io/docs/en/ba
|
|
83
83
|
|
84
84
|
## Middleware mode
|
85
85
|
|
86
|
-
In [middleware mode](https://vitejs.dev/config
|
86
|
+
In [middleware mode](https://vitejs.dev/config/server-options.html#server-middlewaremode), you should make sure your entry `index.html` file is transformed by Vite. Here's an example for an Express server:
|
87
87
|
|
88
88
|
```js
|
89
89
|
app.get('/', async (req, res, next) => {
|
@@ -4,7 +4,7 @@
|
|
4
4
|
* https://github.com/flying-sheep/babel-plugin-transform-react-createelement-to-jsx
|
5
5
|
* @license GNU General Public License v3.0
|
6
6
|
*/
|
7
|
-
function babelRestoreJsx({ types: t }) {
|
7
|
+
function babelRestoreJsx({ types: t }, { reactAlias = "React" }) {
|
8
8
|
function getJSXNode(node) {
|
9
9
|
if (!isReactCreateElement(node)) {
|
10
10
|
return null;
|
@@ -22,8 +22,12 @@ function babelRestoreJsx({ types: t }) {
|
|
22
22
|
if (children == null) {
|
23
23
|
return null;
|
24
24
|
}
|
25
|
-
if (t.isJSXMemberExpression(name) && t.isJSXIdentifier(name.object) && name.object.name ===
|
26
|
-
return t.jsxFragment(
|
25
|
+
if (t.isJSXMemberExpression(name) && t.isJSXIdentifier(name.object) && name.object.name === reactAlias && name.property.name === "Fragment") {
|
26
|
+
return t.jsxFragment(
|
27
|
+
t.jsxOpeningFragment(),
|
28
|
+
t.jsxClosingFragment(),
|
29
|
+
children
|
30
|
+
);
|
27
31
|
}
|
28
32
|
const selfClosing = children.length === 0;
|
29
33
|
const startTag = t.jsxOpeningElement(name, props, selfClosing);
|
@@ -64,7 +68,12 @@ function babelRestoreJsx({ types: t }) {
|
|
64
68
|
if (!isPlainObjectExpression(node)) {
|
65
69
|
return null;
|
66
70
|
}
|
67
|
-
return node.properties.map(
|
71
|
+
return node.properties.map(
|
72
|
+
(prop) => t.isObjectProperty(prop) ? t.jsxAttribute(
|
73
|
+
getJSXIdentifier(prop.key),
|
74
|
+
getJSXAttributeValue(prop.value)
|
75
|
+
) : t.jsxSpreadAttribute(prop.argument)
|
76
|
+
);
|
68
77
|
}
|
69
78
|
function getJSXChild(node) {
|
70
79
|
if (t.isStringLiteral(node)) {
|
@@ -106,9 +115,11 @@ function babelRestoreJsx({ types: t }) {
|
|
106
115
|
}
|
107
116
|
return null;
|
108
117
|
}
|
109
|
-
const isReactCreateElement = (node) => t.isCallExpression(node) && t.isMemberExpression(node.callee) && t.isIdentifier(node.callee.object, { name:
|
118
|
+
const isReactCreateElement = (node) => t.isCallExpression(node) && t.isMemberExpression(node.callee) && t.isIdentifier(node.callee.object, { name: reactAlias }) && t.isIdentifier(node.callee.property, { name: "createElement" }) && !node.callee.computed;
|
110
119
|
const isNullLikeNode = (node) => t.isNullLiteral(node) || t.isIdentifier(node, { name: "undefined" });
|
111
|
-
const isPlainObjectExpression = (node) => t.isObjectExpression(node) && node.properties.every(
|
120
|
+
const isPlainObjectExpression = (node) => t.isObjectExpression(node) && node.properties.every(
|
121
|
+
(property) => t.isSpreadElement(property) || t.isObjectProperty(property, { computed: false }) && getJSXIdentifier(property.key) != null && getJSXAttributeValue(property.value) != null
|
122
|
+
);
|
112
123
|
return {
|
113
124
|
visitor: {
|
114
125
|
CallExpression(path) {
|
@@ -2,7 +2,7 @@
|
|
2
2
|
* https://github.com/flying-sheep/babel-plugin-transform-react-createelement-to-jsx
|
3
3
|
* @license GNU General Public License v3.0
|
4
4
|
*/
|
5
|
-
function babelRestoreJsx({ types: t }) {
|
5
|
+
function babelRestoreJsx({ types: t }, { reactAlias = "React" }) {
|
6
6
|
function getJSXNode(node) {
|
7
7
|
if (!isReactCreateElement(node)) {
|
8
8
|
return null;
|
@@ -20,8 +20,12 @@ function babelRestoreJsx({ types: t }) {
|
|
20
20
|
if (children == null) {
|
21
21
|
return null;
|
22
22
|
}
|
23
|
-
if (t.isJSXMemberExpression(name) && t.isJSXIdentifier(name.object) && name.object.name ===
|
24
|
-
return t.jsxFragment(
|
23
|
+
if (t.isJSXMemberExpression(name) && t.isJSXIdentifier(name.object) && name.object.name === reactAlias && name.property.name === "Fragment") {
|
24
|
+
return t.jsxFragment(
|
25
|
+
t.jsxOpeningFragment(),
|
26
|
+
t.jsxClosingFragment(),
|
27
|
+
children
|
28
|
+
);
|
25
29
|
}
|
26
30
|
const selfClosing = children.length === 0;
|
27
31
|
const startTag = t.jsxOpeningElement(name, props, selfClosing);
|
@@ -62,7 +66,12 @@ function babelRestoreJsx({ types: t }) {
|
|
62
66
|
if (!isPlainObjectExpression(node)) {
|
63
67
|
return null;
|
64
68
|
}
|
65
|
-
return node.properties.map(
|
69
|
+
return node.properties.map(
|
70
|
+
(prop) => t.isObjectProperty(prop) ? t.jsxAttribute(
|
71
|
+
getJSXIdentifier(prop.key),
|
72
|
+
getJSXAttributeValue(prop.value)
|
73
|
+
) : t.jsxSpreadAttribute(prop.argument)
|
74
|
+
);
|
66
75
|
}
|
67
76
|
function getJSXChild(node) {
|
68
77
|
if (t.isStringLiteral(node)) {
|
@@ -104,9 +113,11 @@ function babelRestoreJsx({ types: t }) {
|
|
104
113
|
}
|
105
114
|
return null;
|
106
115
|
}
|
107
|
-
const isReactCreateElement = (node) => t.isCallExpression(node) && t.isMemberExpression(node.callee) && t.isIdentifier(node.callee.object, { name:
|
116
|
+
const isReactCreateElement = (node) => t.isCallExpression(node) && t.isMemberExpression(node.callee) && t.isIdentifier(node.callee.object, { name: reactAlias }) && t.isIdentifier(node.callee.property, { name: "createElement" }) && !node.callee.computed;
|
108
117
|
const isNullLikeNode = (node) => t.isNullLiteral(node) || t.isIdentifier(node, { name: "undefined" });
|
109
|
-
const isPlainObjectExpression = (node) => t.isObjectExpression(node) && node.properties.every(
|
118
|
+
const isPlainObjectExpression = (node) => t.isObjectExpression(node) && node.properties.every(
|
119
|
+
(property) => t.isSpreadElement(property) || t.isObjectProperty(property, { computed: false }) && getJSXIdentifier(property.key) != null && getJSXAttributeValue(property.value) != null
|
120
|
+
);
|
110
121
|
return {
|
111
122
|
visitor: {
|
112
123
|
CallExpression(path) {
|
package/dist/index.cjs
CHANGED
@@ -28,8 +28,13 @@ const fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
|
|
28
28
|
|
29
29
|
const runtimePublicPath = "/@react-refresh";
|
30
30
|
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
|
-
const reactRefreshDir = path__default.dirname(
|
32
|
-
|
31
|
+
const reactRefreshDir = path__default.dirname(
|
32
|
+
_require.resolve("react-refresh/package.json")
|
33
|
+
);
|
34
|
+
const runtimeFilePath = path__default.join(
|
35
|
+
reactRefreshDir,
|
36
|
+
"cjs/react-refresh-runtime.development.js"
|
37
|
+
);
|
33
38
|
const runtimeCode = `
|
34
39
|
const exports = {}
|
35
40
|
${fs__default.readFileSync(runtimeFilePath, "utf-8")}
|
@@ -94,8 +99,12 @@ function isRefreshBoundary(ast) {
|
|
94
99
|
}
|
95
100
|
const { declaration, specifiers } = node;
|
96
101
|
if (declaration) {
|
102
|
+
if (declaration.type === "ClassDeclaration")
|
103
|
+
return false;
|
97
104
|
if (declaration.type === "VariableDeclaration") {
|
98
|
-
return declaration.declarations.every(
|
105
|
+
return declaration.declarations.every(
|
106
|
+
(variable) => isComponentLikeIdentifier(variable.id)
|
107
|
+
);
|
99
108
|
}
|
100
109
|
if (declaration.type === "FunctionDeclaration") {
|
101
110
|
return !!declaration.id && isComponentLikeIdentifier(declaration.id);
|
@@ -119,9 +128,17 @@ function babelImportToRequire({ types: t }) {
|
|
119
128
|
ImportDeclaration(path) {
|
120
129
|
const decl = path.node;
|
121
130
|
const spec = decl.specifiers[0];
|
122
|
-
path.replaceWith(
|
123
|
-
t.
|
124
|
-
|
131
|
+
path.replaceWith(
|
132
|
+
t.variableDeclaration("var", [
|
133
|
+
t.variableDeclarator(
|
134
|
+
spec.local,
|
135
|
+
t.memberExpression(
|
136
|
+
t.callExpression(t.identifier("require"), [decl.source]),
|
137
|
+
spec.imported
|
138
|
+
)
|
139
|
+
)
|
140
|
+
])
|
141
|
+
);
|
125
142
|
}
|
126
143
|
}
|
127
144
|
};
|
@@ -144,20 +161,11 @@ async function restoreJSX(babel, code, filename) {
|
|
144
161
|
if (!reactAlias) {
|
145
162
|
return jsxNotFound;
|
146
163
|
}
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
return "React.Fragment";
|
153
|
-
}).replace(new RegExp(createElementPattern, "g"), (original, component) => {
|
154
|
-
if (/^[a-z][\w$]*$/.test(component)) {
|
155
|
-
return original;
|
156
|
-
}
|
157
|
-
hasCompiledJsx = true;
|
158
|
-
return "React.createElement(" + (component === "Fragment" ? "React.Fragment" : component);
|
159
|
-
});
|
160
|
-
if (!hasCompiledJsx) {
|
164
|
+
const reactJsxRE = new RegExp(
|
165
|
+
`\\b${reactAlias}\\.(createElement|Fragment)\\b`,
|
166
|
+
"g"
|
167
|
+
);
|
168
|
+
if (!reactJsxRE.test(code)) {
|
161
169
|
return jsxNotFound;
|
162
170
|
}
|
163
171
|
const result = await babel.transformAsync(code, {
|
@@ -169,12 +177,14 @@ async function restoreJSX(babel, code, filename) {
|
|
169
177
|
parserOpts: {
|
170
178
|
plugins: ["jsx"]
|
171
179
|
},
|
172
|
-
plugins: [await getBabelRestoreJSX()]
|
180
|
+
plugins: [[await getBabelRestoreJSX(), { reactAlias }]]
|
173
181
|
});
|
174
182
|
return [result?.ast, isCommonJS];
|
175
183
|
}
|
176
184
|
function parseReactAlias(code) {
|
177
|
-
let match = code.match(
|
185
|
+
let match = code.match(
|
186
|
+
/\b(var|let|const)\s+([^=\{\s]+)\s*=\s*require\(["']react["']\)/
|
187
|
+
);
|
178
188
|
if (match) {
|
179
189
|
return [match[2], true];
|
180
190
|
}
|
@@ -227,12 +237,16 @@ function viteReact(opts = {}) {
|
|
227
237
|
const jsxInject = config.esbuild && config.esbuild.jsxInject;
|
228
238
|
if (jsxInject && importReactRE.test(jsxInject)) {
|
229
239
|
skipReactImport = true;
|
230
|
-
config.logger.warn(
|
240
|
+
config.logger.warn(
|
241
|
+
"[@vitejs/plugin-react] This plugin imports React for you automatically, so you can stop using `esbuild.jsxInject` for that purpose."
|
242
|
+
);
|
231
243
|
}
|
232
244
|
config.plugins.forEach((plugin) => {
|
233
245
|
const hasConflict = plugin.name === "react-refresh" || plugin !== viteReactJsx && plugin.name === "vite:react-jsx";
|
234
246
|
if (hasConflict)
|
235
|
-
return config.logger.warn(
|
247
|
+
return config.logger.warn(
|
248
|
+
`[@vitejs/plugin-react] You should stop using "${plugin.name}" since this plugin conflicts with it.`
|
249
|
+
);
|
236
250
|
});
|
237
251
|
runPluginOverrides = (babelOptions, context) => {
|
238
252
|
const hooks = config.plugins.map((plugin) => plugin.api?.reactBabel).filter(Boolean);
|
@@ -285,7 +299,9 @@ function viteReact(opts = {}) {
|
|
285
299
|
const [restoredAst, isCommonJS] = !isProjectFile && !isJSX && !isOptimizedReactDom ? await restoreJSX(babel__namespace, code, id) : [null, false];
|
286
300
|
if (isJSX || (ast = restoredAst)) {
|
287
301
|
plugins.push([
|
288
|
-
await loadPlugin(
|
302
|
+
await loadPlugin(
|
303
|
+
"@babel/plugin-transform-react-jsx" + (isProduction ? "" : "-development")
|
304
|
+
),
|
289
305
|
{
|
290
306
|
runtime: "automatic",
|
291
307
|
importSource: opts.jsxImportSource,
|
@@ -298,7 +314,10 @@ function viteReact(opts = {}) {
|
|
298
314
|
}
|
299
315
|
} else if (isProjectFile) {
|
300
316
|
if (!isProduction) {
|
301
|
-
plugins.push(
|
317
|
+
plugins.push(
|
318
|
+
await loadPlugin("@babel/plugin-transform-react-jsx-self"),
|
319
|
+
await loadPlugin("@babel/plugin-transform-react-jsx-source")
|
320
|
+
);
|
302
321
|
}
|
303
322
|
if (!skipReactImport && !importReactRE.test(code)) {
|
304
323
|
prependReactImport = true;
|
package/dist/index.mjs
CHANGED
@@ -7,8 +7,13 @@ import { createRequire } from 'node:module';
|
|
7
7
|
|
8
8
|
const runtimePublicPath = "/@react-refresh";
|
9
9
|
const _require = createRequire(import.meta.url);
|
10
|
-
const reactRefreshDir = path.dirname(
|
11
|
-
|
10
|
+
const reactRefreshDir = path.dirname(
|
11
|
+
_require.resolve("react-refresh/package.json")
|
12
|
+
);
|
13
|
+
const runtimeFilePath = path.join(
|
14
|
+
reactRefreshDir,
|
15
|
+
"cjs/react-refresh-runtime.development.js"
|
16
|
+
);
|
12
17
|
const runtimeCode = `
|
13
18
|
const exports = {}
|
14
19
|
${fs.readFileSync(runtimeFilePath, "utf-8")}
|
@@ -73,8 +78,12 @@ function isRefreshBoundary(ast) {
|
|
73
78
|
}
|
74
79
|
const { declaration, specifiers } = node;
|
75
80
|
if (declaration) {
|
81
|
+
if (declaration.type === "ClassDeclaration")
|
82
|
+
return false;
|
76
83
|
if (declaration.type === "VariableDeclaration") {
|
77
|
-
return declaration.declarations.every(
|
84
|
+
return declaration.declarations.every(
|
85
|
+
(variable) => isComponentLikeIdentifier(variable.id)
|
86
|
+
);
|
78
87
|
}
|
79
88
|
if (declaration.type === "FunctionDeclaration") {
|
80
89
|
return !!declaration.id && isComponentLikeIdentifier(declaration.id);
|
@@ -98,9 +107,17 @@ function babelImportToRequire({ types: t }) {
|
|
98
107
|
ImportDeclaration(path) {
|
99
108
|
const decl = path.node;
|
100
109
|
const spec = decl.specifiers[0];
|
101
|
-
path.replaceWith(
|
102
|
-
t.
|
103
|
-
|
110
|
+
path.replaceWith(
|
111
|
+
t.variableDeclaration("var", [
|
112
|
+
t.variableDeclarator(
|
113
|
+
spec.local,
|
114
|
+
t.memberExpression(
|
115
|
+
t.callExpression(t.identifier("require"), [decl.source]),
|
116
|
+
spec.imported
|
117
|
+
)
|
118
|
+
)
|
119
|
+
])
|
120
|
+
);
|
104
121
|
}
|
105
122
|
}
|
106
123
|
};
|
@@ -123,20 +140,11 @@ async function restoreJSX(babel, code, filename) {
|
|
123
140
|
if (!reactAlias) {
|
124
141
|
return jsxNotFound;
|
125
142
|
}
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
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) {
|
143
|
+
const reactJsxRE = new RegExp(
|
144
|
+
`\\b${reactAlias}\\.(createElement|Fragment)\\b`,
|
145
|
+
"g"
|
146
|
+
);
|
147
|
+
if (!reactJsxRE.test(code)) {
|
140
148
|
return jsxNotFound;
|
141
149
|
}
|
142
150
|
const result = await babel.transformAsync(code, {
|
@@ -148,12 +156,14 @@ async function restoreJSX(babel, code, filename) {
|
|
148
156
|
parserOpts: {
|
149
157
|
plugins: ["jsx"]
|
150
158
|
},
|
151
|
-
plugins: [await getBabelRestoreJSX()]
|
159
|
+
plugins: [[await getBabelRestoreJSX(), { reactAlias }]]
|
152
160
|
});
|
153
161
|
return [result?.ast, isCommonJS];
|
154
162
|
}
|
155
163
|
function parseReactAlias(code) {
|
156
|
-
let match = code.match(
|
164
|
+
let match = code.match(
|
165
|
+
/\b(var|let|const)\s+([^=\{\s]+)\s*=\s*require\(["']react["']\)/
|
166
|
+
);
|
157
167
|
if (match) {
|
158
168
|
return [match[2], true];
|
159
169
|
}
|
@@ -206,12 +216,16 @@ function viteReact(opts = {}) {
|
|
206
216
|
const jsxInject = config.esbuild && config.esbuild.jsxInject;
|
207
217
|
if (jsxInject && importReactRE.test(jsxInject)) {
|
208
218
|
skipReactImport = true;
|
209
|
-
config.logger.warn(
|
219
|
+
config.logger.warn(
|
220
|
+
"[@vitejs/plugin-react] This plugin imports React for you automatically, so you can stop using `esbuild.jsxInject` for that purpose."
|
221
|
+
);
|
210
222
|
}
|
211
223
|
config.plugins.forEach((plugin) => {
|
212
224
|
const hasConflict = plugin.name === "react-refresh" || plugin !== viteReactJsx && plugin.name === "vite:react-jsx";
|
213
225
|
if (hasConflict)
|
214
|
-
return config.logger.warn(
|
226
|
+
return config.logger.warn(
|
227
|
+
`[@vitejs/plugin-react] You should stop using "${plugin.name}" since this plugin conflicts with it.`
|
228
|
+
);
|
215
229
|
});
|
216
230
|
runPluginOverrides = (babelOptions, context) => {
|
217
231
|
const hooks = config.plugins.map((plugin) => plugin.api?.reactBabel).filter(Boolean);
|
@@ -264,7 +278,9 @@ function viteReact(opts = {}) {
|
|
264
278
|
const [restoredAst, isCommonJS] = !isProjectFile && !isJSX && !isOptimizedReactDom ? await restoreJSX(babel, code, id) : [null, false];
|
265
279
|
if (isJSX || (ast = restoredAst)) {
|
266
280
|
plugins.push([
|
267
|
-
await loadPlugin(
|
281
|
+
await loadPlugin(
|
282
|
+
"@babel/plugin-transform-react-jsx" + (isProduction ? "" : "-development")
|
283
|
+
),
|
268
284
|
{
|
269
285
|
runtime: "automatic",
|
270
286
|
importSource: opts.jsxImportSource,
|
@@ -277,7 +293,10 @@ function viteReact(opts = {}) {
|
|
277
293
|
}
|
278
294
|
} else if (isProjectFile) {
|
279
295
|
if (!isProduction) {
|
280
|
-
plugins.push(
|
296
|
+
plugins.push(
|
297
|
+
await loadPlugin("@babel/plugin-transform-react-jsx-self"),
|
298
|
+
await loadPlugin("@babel/plugin-transform-react-jsx-source")
|
299
|
+
);
|
281
300
|
}
|
282
301
|
if (!skipReactImport && !importReactRE.test(code)) {
|
283
302
|
prependReactImport = true;
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@vitejs/plugin-react",
|
3
|
-
"version": "2.0.
|
3
|
+
"version": "2.0.1",
|
4
4
|
"license": "MIT",
|
5
5
|
"author": "Evan You",
|
6
6
|
"contributors": [
|
@@ -27,7 +27,7 @@
|
|
27
27
|
"prepublishOnly": "npm run build"
|
28
28
|
},
|
29
29
|
"engines": {
|
30
|
-
"node": "
|
30
|
+
"node": "^14.18.0 || >=16.0.0"
|
31
31
|
},
|
32
32
|
"repository": {
|
33
33
|
"type": "git",
|
@@ -39,8 +39,8 @@
|
|
39
39
|
},
|
40
40
|
"homepage": "https://github.com/vitejs/vite/tree/main/packages/plugin-react#readme",
|
41
41
|
"dependencies": {
|
42
|
-
"@babel/core": "^7.18.
|
43
|
-
"@babel/plugin-transform-react-jsx": "^7.18.
|
42
|
+
"@babel/core": "^7.18.10",
|
43
|
+
"@babel/plugin-transform-react-jsx": "^7.18.10",
|
44
44
|
"@babel/plugin-transform-react-jsx-development": "^7.18.6",
|
45
45
|
"@babel/plugin-transform-react-jsx-self": "^7.18.6",
|
46
46
|
"@babel/plugin-transform-react-jsx-source": "^7.18.6",
|
package/src/fast-refresh.ts
CHANGED
@@ -92,6 +92,7 @@ export function isRefreshBoundary(ast: t.File): boolean {
|
|
92
92
|
}
|
93
93
|
const { declaration, specifiers } = node
|
94
94
|
if (declaration) {
|
95
|
+
if (declaration.type === 'ClassDeclaration') return false
|
95
96
|
if (declaration.type === 'VariableDeclaration') {
|
96
97
|
return declaration.declarations.every((variable) =>
|
97
98
|
isComponentLikeIdentifier(variable.id)
|
@@ -4,6 +4,10 @@
|
|
4
4
|
*/
|
5
5
|
import type * as babel from '@babel/core'
|
6
6
|
|
7
|
+
interface PluginOptions {
|
8
|
+
reactAlias: string
|
9
|
+
}
|
10
|
+
|
7
11
|
/**
|
8
12
|
* Visitor factory for babel, converting React.createElement(...) to <jsx ...>...</jsx>
|
9
13
|
*
|
@@ -17,7 +21,10 @@ import type * as babel from '@babel/core'
|
|
17
21
|
*
|
18
22
|
* Any of those arguments might also be missing (undefined) and/or invalid.
|
19
23
|
*/
|
20
|
-
export default function (
|
24
|
+
export default function (
|
25
|
+
{ types: t }: typeof babel,
|
26
|
+
{ reactAlias = 'React' }: PluginOptions
|
27
|
+
): babel.PluginObj {
|
21
28
|
/**
|
22
29
|
* Get a `JSXElement` from a `CallExpression`.
|
23
30
|
* Returns `null` if this impossible.
|
@@ -48,7 +55,7 @@ export default function ({ types: t }: typeof babel): babel.PluginObj {
|
|
48
55
|
if (
|
49
56
|
t.isJSXMemberExpression(name) &&
|
50
57
|
t.isJSXIdentifier(name.object) &&
|
51
|
-
name.object.name ===
|
58
|
+
name.object.name === reactAlias &&
|
52
59
|
name.property.name === 'Fragment'
|
53
60
|
) {
|
54
61
|
return t.jsxFragment(
|
@@ -182,7 +189,7 @@ export default function ({ types: t }: typeof babel): babel.PluginObj {
|
|
182
189
|
const isReactCreateElement = (node: any) =>
|
183
190
|
t.isCallExpression(node) &&
|
184
191
|
t.isMemberExpression(node.callee) &&
|
185
|
-
t.isIdentifier(node.callee.object, { name:
|
192
|
+
t.isIdentifier(node.callee.object, { name: reactAlias }) &&
|
186
193
|
t.isIdentifier(node.callee.property, { name: 'createElement' }) &&
|
187
194
|
!node.callee.computed
|
188
195
|
|
@@ -81,7 +81,10 @@ describe('restore-jsx', () => {
|
|
81
81
|
expect(
|
82
82
|
await jsx(`import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react';
|
83
83
|
React__default.createElement(foo)`)
|
84
|
-
).
|
84
|
+
).toMatchInlineSnapshot(`
|
85
|
+
"import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react';
|
86
|
+
React__default.createElement(foo);"
|
87
|
+
`)
|
85
88
|
expect(
|
86
89
|
await jsx(`import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react';
|
87
90
|
React__default.createElement("h1")`)
|
@@ -104,7 +107,12 @@ describe('restore-jsx', () => {
|
|
104
107
|
expect(
|
105
108
|
await jsx(`import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react';
|
106
109
|
React__default.createElement(foo, {hi: there})`)
|
107
|
-
).
|
110
|
+
).toMatchInlineSnapshot(`
|
111
|
+
"import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react';
|
112
|
+
React__default.createElement(foo, {
|
113
|
+
hi: there
|
114
|
+
});"
|
115
|
+
`)
|
108
116
|
expect(
|
109
117
|
await jsx(`import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react';
|
110
118
|
React__default.createElement("h1", {hi: there})`)
|
@@ -114,4 +122,26 @@ describe('restore-jsx', () => {
|
|
114
122
|
React__default.createElement(Foo, {hi: there})`)
|
115
123
|
).toMatch(`<Foo hi={there} />;`)
|
116
124
|
})
|
125
|
+
|
126
|
+
it('should handle Fragment', async () => {
|
127
|
+
expect(
|
128
|
+
await jsx(`import R, { Fragment } from 'react';
|
129
|
+
R.createElement(Fragment)
|
130
|
+
`)
|
131
|
+
).toMatchInlineSnapshot(`
|
132
|
+
"import R, { Fragment } from 'react';
|
133
|
+
<Fragment />;"
|
134
|
+
`)
|
135
|
+
})
|
136
|
+
|
137
|
+
it('should handle Fragment alias', async () => {
|
138
|
+
expect(
|
139
|
+
await jsx(`import RA, { Fragment as F } from 'react';
|
140
|
+
RA.createElement(F, null, RA.createElement(RA.Fragment))
|
141
|
+
`)
|
142
|
+
).toMatchInlineSnapshot(`
|
143
|
+
"import RA, { Fragment as F } from 'react';
|
144
|
+
<F><></></F>;"
|
145
|
+
`)
|
146
|
+
})
|
117
147
|
})
|
@@ -33,34 +33,12 @@ export async function restoreJSX(
|
|
33
33
|
return jsxNotFound
|
34
34
|
}
|
35
35
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
// Replace the alias with "React" so JSX can be reverse compiled.
|
42
|
-
code = code
|
43
|
-
.replace(new RegExp(fragmentPattern, 'g'), () => {
|
44
|
-
hasCompiledJsx = true
|
45
|
-
return 'React.Fragment'
|
46
|
-
})
|
47
|
-
.replace(new RegExp(createElementPattern, 'g'), (original, component) => {
|
48
|
-
if (/^[a-z][\w$]*$/.test(component)) {
|
49
|
-
// Take care not to replace the alias for `createElement` calls whose
|
50
|
-
// component is a lowercased variable, since the `restoreJSX` Babel
|
51
|
-
// plugin leaves them untouched.
|
52
|
-
return original
|
53
|
-
}
|
54
|
-
hasCompiledJsx = true
|
55
|
-
return (
|
56
|
-
'React.createElement(' +
|
57
|
-
// Assume `Fragment` is equivalent to `React.Fragment` so modules
|
58
|
-
// that use `import {Fragment} from 'react'` are reverse compiled.
|
59
|
-
(component === 'Fragment' ? 'React.Fragment' : component)
|
60
|
-
)
|
61
|
-
})
|
36
|
+
const reactJsxRE = new RegExp(
|
37
|
+
`\\b${reactAlias}\\.(createElement|Fragment)\\b`,
|
38
|
+
'g'
|
39
|
+
)
|
62
40
|
|
63
|
-
if (!
|
41
|
+
if (!reactJsxRE.test(code)) {
|
64
42
|
return jsxNotFound
|
65
43
|
}
|
66
44
|
|
@@ -73,7 +51,7 @@ export async function restoreJSX(
|
|
73
51
|
parserOpts: {
|
74
52
|
plugins: ['jsx']
|
75
53
|
},
|
76
|
-
plugins: [await getBabelRestoreJSX()]
|
54
|
+
plugins: [[await getBabelRestoreJSX(), { reactAlias }]]
|
77
55
|
})
|
78
56
|
|
79
57
|
return [result?.ast, isCommonJS]
|