diff --git a/packages/plugin-react/src/jsx-runtime/babel-restore-jsx.ts b/packages/plugin-react/src/jsx-runtime/babel-restore-jsx.ts
index 669a0aeeced207..91b4db5411bf26 100644
--- a/packages/plugin-react/src/jsx-runtime/babel-restore-jsx.ts
+++ b/packages/plugin-react/src/jsx-runtime/babel-restore-jsx.ts
@@ -4,6 +4,10 @@
*/
import type * as babel from '@babel/core'
+interface PluginOptions {
+ reactAlias: string
+}
+
/**
* Visitor factory for babel, converting React.createElement(...) to ...
*
@@ -17,7 +21,10 @@ import type * as babel from '@babel/core'
*
* Any of those arguments might also be missing (undefined) and/or invalid.
*/
-export default function ({ types: t }: typeof babel): babel.PluginObj {
+export default function (
+ { types: t }: typeof babel,
+ { reactAlias = 'React' }: PluginOptions
+): babel.PluginObj {
/**
* Get a `JSXElement` from a `CallExpression`.
* Returns `null` if this impossible.
@@ -48,7 +55,7 @@ export default function ({ types: t }: typeof babel): babel.PluginObj {
if (
t.isJSXMemberExpression(name) &&
t.isJSXIdentifier(name.object) &&
- name.object.name === 'React' &&
+ name.object.name === reactAlias &&
name.property.name === 'Fragment'
) {
return t.jsxFragment(
@@ -182,7 +189,7 @@ export default function ({ types: t }: typeof babel): babel.PluginObj {
const isReactCreateElement = (node: any) =>
t.isCallExpression(node) &&
t.isMemberExpression(node.callee) &&
- t.isIdentifier(node.callee.object, { name: 'React' }) &&
+ t.isIdentifier(node.callee.object, { name: reactAlias }) &&
t.isIdentifier(node.callee.property, { name: 'createElement' }) &&
!node.callee.computed
diff --git a/packages/plugin-react/src/jsx-runtime/restore-jsx.spec.ts b/packages/plugin-react/src/jsx-runtime/restore-jsx.spec.ts
index 7d5b14bfc9cfd4..00ea39673ec415 100644
--- a/packages/plugin-react/src/jsx-runtime/restore-jsx.spec.ts
+++ b/packages/plugin-react/src/jsx-runtime/restore-jsx.spec.ts
@@ -81,7 +81,10 @@ describe('restore-jsx', () => {
expect(
await jsx(`import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react';
React__default.createElement(foo)`)
- ).toBeNull()
+ ).toMatchInlineSnapshot(`
+ "import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react';
+ React__default.createElement(foo);"
+ `)
expect(
await jsx(`import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react';
React__default.createElement("h1")`)
@@ -104,7 +107,12 @@ describe('restore-jsx', () => {
expect(
await jsx(`import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react';
React__default.createElement(foo, {hi: there})`)
- ).toBeNull()
+ ).toMatchInlineSnapshot(`
+ "import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react';
+ React__default.createElement(foo, {
+ hi: there
+ });"
+ `)
expect(
await jsx(`import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react';
React__default.createElement("h1", {hi: there})`)
@@ -114,4 +122,26 @@ describe('restore-jsx', () => {
React__default.createElement(Foo, {hi: there})`)
).toMatch(`;`)
})
+
+ it('should handle Fragment', async () => {
+ expect(
+ await jsx(`import R, { Fragment } from 'react';
+ R.createElement(Fragment)
+ `)
+ ).toMatchInlineSnapshot(`
+ "import R, { Fragment } from 'react';
+ ;"
+ `)
+ })
+
+ it('should handle Fragment alias', async () => {
+ expect(
+ await jsx(`import RA, { Fragment as F } from 'react';
+ RA.createElement(F, null, RA.createElement(RA.Fragment))
+ `)
+ ).toMatchInlineSnapshot(`
+ "import RA, { Fragment as F } from 'react';
+ <>>;"
+ `)
+ })
})
diff --git a/packages/plugin-react/src/jsx-runtime/restore-jsx.ts b/packages/plugin-react/src/jsx-runtime/restore-jsx.ts
index d67a54e8f08aad..ddc3c1530e0d9f 100644
--- a/packages/plugin-react/src/jsx-runtime/restore-jsx.ts
+++ b/packages/plugin-react/src/jsx-runtime/restore-jsx.ts
@@ -33,34 +33,12 @@ export async function restoreJSX(
return jsxNotFound
}
- let hasCompiledJsx = false
-
- const fragmentPattern = `\\b${reactAlias}\\.Fragment\\b`
- const createElementPattern = `\\b${reactAlias}\\.createElement\\(\\s*([A-Z"'][\\w$.]*["']?)`
-
- // Replace the alias with "React" so JSX can be reverse compiled.
- code = code
- .replace(new RegExp(fragmentPattern, 'g'), () => {
- hasCompiledJsx = true
- return 'React.Fragment'
- })
- .replace(new RegExp(createElementPattern, 'g'), (original, component) => {
- if (/^[a-z][\w$]*$/.test(component)) {
- // Take care not to replace the alias for `createElement` calls whose
- // component is a lowercased variable, since the `restoreJSX` Babel
- // plugin leaves them untouched.
- return original
- }
- hasCompiledJsx = true
- return (
- 'React.createElement(' +
- // Assume `Fragment` is equivalent to `React.Fragment` so modules
- // that use `import {Fragment} from 'react'` are reverse compiled.
- (component === 'Fragment' ? 'React.Fragment' : component)
- )
- })
+ const reactJsxRE = new RegExp(
+ `\\b${reactAlias}\\.(createElement|Fragment)\\b`,
+ 'g'
+ )
- if (!hasCompiledJsx) {
+ if (!reactJsxRE.test(code)) {
return jsxNotFound
}
@@ -73,7 +51,7 @@ export async function restoreJSX(
parserOpts: {
plugins: ['jsx']
},
- plugins: [await getBabelRestoreJSX()]
+ plugins: [[await getBabelRestoreJSX(), { reactAlias }]]
})
return [result?.ast, isCommonJS]