@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 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/#server-middlewaremode), you should make sure your entry `index.html` file is transformed by Vite. Here's an example for an Express server:
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 === "React" && name.property.name === "Fragment") {
26
- return t.jsxFragment(t.jsxOpeningFragment(), t.jsxClosingFragment(), children);
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((prop) => t.isObjectProperty(prop) ? t.jsxAttribute(getJSXIdentifier(prop.key), getJSXAttributeValue(prop.value)) : t.jsxSpreadAttribute(prop.argument));
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: "React" }) && t.isIdentifier(node.callee.property, { name: "createElement" }) && !node.callee.computed;
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((property) => t.isSpreadElement(property) || t.isObjectProperty(property, { computed: false }) && getJSXIdentifier(property.key) != null && getJSXAttributeValue(property.value) != null);
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 === "React" && name.property.name === "Fragment") {
24
- return t.jsxFragment(t.jsxOpeningFragment(), t.jsxClosingFragment(), children);
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((prop) => t.isObjectProperty(prop) ? t.jsxAttribute(getJSXIdentifier(prop.key), getJSXAttributeValue(prop.value)) : t.jsxSpreadAttribute(prop.argument));
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: "React" }) && t.isIdentifier(node.callee.property, { name: "createElement" }) && !node.callee.computed;
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((property) => t.isSpreadElement(property) || t.isObjectProperty(property, { computed: false }) && getJSXIdentifier(property.key) != null && getJSXAttributeValue(property.value) != null);
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(_require.resolve("react-refresh/package.json"));
32
- const runtimeFilePath = path__default.join(reactRefreshDir, "cjs/react-refresh-runtime.development.js");
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((variable) => isComponentLikeIdentifier(variable.id));
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(t.variableDeclaration("var", [
123
- t.variableDeclarator(spec.local, t.memberExpression(t.callExpression(t.identifier("require"), [decl.source]), spec.imported))
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
- let hasCompiledJsx = false;
148
- const fragmentPattern = `\\b${reactAlias}\\.Fragment\\b`;
149
- const createElementPattern = `\\b${reactAlias}\\.createElement\\(\\s*([A-Z"'][\\w$.]*["']?)`;
150
- code = code.replace(new RegExp(fragmentPattern, "g"), () => {
151
- hasCompiledJsx = true;
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(/\b(var|let|const)\s+([^=\{\s]+)\s*=\s*require\(["']react["']\)/);
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("[@vitejs/plugin-react] This plugin imports React for you automatically, so you can stop using `esbuild.jsxInject` for that purpose.");
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(`[@vitejs/plugin-react] You should stop using "${plugin.name}" since this plugin conflicts with it.`);
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("@babel/plugin-transform-react-jsx" + (isProduction ? "" : "-development")),
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(await loadPlugin("@babel/plugin-transform-react-jsx-self"), await loadPlugin("@babel/plugin-transform-react-jsx-source"));
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(_require.resolve("react-refresh/package.json"));
11
- const runtimeFilePath = path.join(reactRefreshDir, "cjs/react-refresh-runtime.development.js");
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((variable) => isComponentLikeIdentifier(variable.id));
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(t.variableDeclaration("var", [
102
- t.variableDeclarator(spec.local, t.memberExpression(t.callExpression(t.identifier("require"), [decl.source]), spec.imported))
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
- 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) {
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(/\b(var|let|const)\s+([^=\{\s]+)\s*=\s*require\(["']react["']\)/);
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("[@vitejs/plugin-react] This plugin imports React for you automatically, so you can stop using `esbuild.jsxInject` for that purpose.");
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(`[@vitejs/plugin-react] You should stop using "${plugin.name}" since this plugin conflicts with it.`);
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("@babel/plugin-transform-react-jsx" + (isProduction ? "" : "-development")),
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(await loadPlugin("@babel/plugin-transform-react-jsx-self"), await loadPlugin("@babel/plugin-transform-react-jsx-source"));
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.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": ">=14.18.0"
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.6",
43
- "@babel/plugin-transform-react-jsx": "^7.18.6",
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",
@@ -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 ({ types: t }: typeof babel): babel.PluginObj {
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 === 'React' &&
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: 'React' }) &&
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
- ).toBeNull()
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
- ).toBeNull()
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
- let hasCompiledJsx = false
37
-
38
- const fragmentPattern = `\\b${reactAlias}\\.Fragment\\b`
39
- const createElementPattern = `\\b${reactAlias}\\.createElement\\(\\s*([A-Z"'][\\w$.]*["']?)`
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 (!hasCompiledJsx) {
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]