@vitejs/plugin-react 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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,14 @@ 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
+ ).filter(
77
+ (prop) => t.isJSXIdentifier(prop.name) ? prop.name.name !== "__self" && prop.name.name !== "__source" : true
78
+ );
68
79
  }
69
80
  function getJSXChild(node) {
70
81
  if (t.isStringLiteral(node)) {
@@ -106,9 +117,11 @@ function babelRestoreJsx({ types: t }) {
106
117
  }
107
118
  return null;
108
119
  }
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;
120
+ 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
121
  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);
122
+ const isPlainObjectExpression = (node) => t.isObjectExpression(node) && node.properties.every(
123
+ (property) => t.isSpreadElement(property) || t.isObjectProperty(property, { computed: false }) && getJSXIdentifier(property.key) != null && getJSXAttributeValue(property.value) != null
124
+ );
112
125
  return {
113
126
  visitor: {
114
127
  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,14 @@ 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
+ ).filter(
75
+ (prop) => t.isJSXIdentifier(prop.name) ? prop.name.name !== "__self" && prop.name.name !== "__source" : true
76
+ );
66
77
  }
67
78
  function getJSXChild(node) {
68
79
  if (t.isStringLiteral(node)) {
@@ -104,9 +115,11 @@ function babelRestoreJsx({ types: t }) {
104
115
  }
105
116
  return null;
106
117
  }
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;
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;
108
119
  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);
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
+ );
110
123
  return {
111
124
  visitor: {
112
125
  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;
@@ -412,7 +431,7 @@ function viteReact(opts = {}) {
412
431
  config() {
413
432
  return {
414
433
  optimizeDeps: {
415
- include: [reactJsxRuntimeId, reactJsxDevRuntimeId]
434
+ include: [reactJsxRuntimeId, reactJsxDevRuntimeId, "react"]
416
435
  }
417
436
  };
418
437
  },
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;
@@ -391,7 +410,7 @@ function viteReact(opts = {}) {
391
410
  config() {
392
411
  return {
393
412
  optimizeDeps: {
394
- include: [reactJsxRuntimeId, reactJsxDevRuntimeId]
413
+ include: [reactJsxRuntimeId, reactJsxDevRuntimeId, "react"]
395
414
  }
396
415
  };
397
416
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vitejs/plugin-react",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
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.13",
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)
package/src/index.ts CHANGED
@@ -406,7 +406,10 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
406
406
  config() {
407
407
  return {
408
408
  optimizeDeps: {
409
- include: [reactJsxRuntimeId, reactJsxDevRuntimeId]
409
+ // We can't add `react-dom` because the dependency is `react-dom/client`
410
+ // for React 18 while it's `react-dom` for React 17. We'd need to detect
411
+ // what React version the user has installed.
412
+ include: [reactJsxRuntimeId, reactJsxDevRuntimeId, 'react']
410
413
  }
411
414
  }
412
415
  },
@@ -115,4 +115,18 @@ describe('babel-restore-jsx', () => {
115
115
  `"React.createElement(aaa);"`
116
116
  )
117
117
  })
118
+
119
+ it('should not handle contains __self prop', () => {
120
+ expect(
121
+ jsx('React.createElement(Provider, { __self: this })')
122
+ ).toMatchInlineSnapshot('"<Provider />;"')
123
+ })
124
+
125
+ it('should not handle contains __source prop', () => {
126
+ expect(
127
+ jsx(
128
+ 'React.createElement(Provider, { __source: { fileName: _jsxFileName, lineNumber: 133 }})'
129
+ )
130
+ ).toMatchInlineSnapshot('"<Provider />;"')
131
+ })
118
132
  })
@@ -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(
@@ -69,7 +76,7 @@ export default function ({ types: t }: typeof babel): babel.PluginObj {
69
76
 
70
77
  /**
71
78
  * Get a JSXIdentifier or JSXMemberExpression from a Node of known type.
72
- * Returns null if a unknown node type, null or undefined is passed.
79
+ * Returns null if an unknown node type, null or undefined is passed.
73
80
  */
74
81
  function getJSXName(node: any): any {
75
82
  if (node == null) {
@@ -93,7 +100,7 @@ export default function ({ types: t }: typeof babel): babel.PluginObj {
93
100
  }
94
101
 
95
102
  /**
96
- * Get a array of JSX(Spread)Attribute from a props ObjectExpression.
103
+ * Get an array of JSX(Spread)Attribute from a props ObjectExpression.
97
104
  * Handles the _extends Expression babel creates from SpreadElement nodes.
98
105
  * Returns null if a validation error occurs.
99
106
  */
@@ -119,14 +126,20 @@ export default function ({ types: t }: typeof babel): babel.PluginObj {
119
126
  if (!isPlainObjectExpression(node)) {
120
127
  return null
121
128
  }
122
- return node.properties.map((prop: any) =>
123
- t.isObjectProperty(prop)
124
- ? t.jsxAttribute(
125
- getJSXIdentifier(prop.key)!,
126
- getJSXAttributeValue(prop.value)
127
- )
128
- : t.jsxSpreadAttribute(prop.argument)
129
- )
129
+ return node.properties
130
+ .map((prop: any) =>
131
+ t.isObjectProperty(prop)
132
+ ? t.jsxAttribute(
133
+ getJSXIdentifier(prop.key)!,
134
+ getJSXAttributeValue(prop.value)
135
+ )
136
+ : t.jsxSpreadAttribute(prop.argument)
137
+ )
138
+ .filter((prop: any) =>
139
+ t.isJSXIdentifier(prop.name)
140
+ ? prop.name.name !== '__self' && prop.name.name !== '__source'
141
+ : true
142
+ )
130
143
  }
131
144
 
132
145
  function getJSXChild(node: any) {
@@ -182,7 +195,7 @@ export default function ({ types: t }: typeof babel): babel.PluginObj {
182
195
  const isReactCreateElement = (node: any) =>
183
196
  t.isCallExpression(node) &&
184
197
  t.isMemberExpression(node.callee) &&
185
- t.isIdentifier(node.callee.object, { name: 'React' }) &&
198
+ t.isIdentifier(node.callee.object, { name: reactAlias }) &&
186
199
  t.isIdentifier(node.callee.property, { name: 'createElement' }) &&
187
200
  !node.callee.computed
188
201
 
@@ -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]