From bf40e5c88db4281b035f67d1789cccf66333ce23 Mon Sep 17 00:00:00 2001
From: xyl66 <492113976@qq.com>
Date: Mon, 2 May 2022 17:25:24 +0800
Subject: [PATCH] fix(plugin-react): React is not defined when component name
is lowercase (#6838)
---
.../src/jsx-runtime/restore-jsx.spec.ts | 55 +++++++++++++++++++
.../src/jsx-runtime/restore-jsx.ts | 42 ++++++++------
2 files changed, 81 insertions(+), 16 deletions(-)
create mode 100644 packages/plugin-react/src/jsx-runtime/restore-jsx.spec.ts
diff --git a/packages/plugin-react/src/jsx-runtime/restore-jsx.spec.ts b/packages/plugin-react/src/jsx-runtime/restore-jsx.spec.ts
new file mode 100644
index 00000000000000..adbd7a60599c25
--- /dev/null
+++ b/packages/plugin-react/src/jsx-runtime/restore-jsx.spec.ts
@@ -0,0 +1,55 @@
+import { restoreJSX } from './restore-jsx'
+import * as babel from '@babel/core'
+
+async function jsx(sourceCode: string) {
+ const [ast] = await restoreJSX(babel, sourceCode, 'test.js')
+ if (ast === null) {
+ return ast
+ }
+ const { code } = await babel.transformFromAstAsync(ast, null, {
+ configFile: false
+ })
+ return code
+}
+// jsx(`import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react';
+// React__default.createElement(Foo)`)
+// Tests adapted from: https://github.com/flying-sheep/babel-plugin-transform-react-createelement-to-jsx/blob/63137b6/test/index.js
+describe('restore-jsx', () => {
+ it('should trans to ', async () => {
+ expect(
+ await jsx(`import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react';
+ React__default.createElement(foo)`)
+ ).toBeNull()
+ expect(
+ await jsx(`import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react';
+ React__default.createElement("h1")`)
+ ).toMatch(`
;`)
+ expect(
+ await jsx(`import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react';
+ React__default.createElement(Foo)`)
+ ).toMatch(`;`)
+ expect(
+ await jsx(`import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react';
+ React__default.createElement(Foo.Bar)`)
+ ).toMatch(`;`)
+ expect(
+ await jsx(`import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react';
+ React__default.createElement(Foo.Bar.Baz)`)
+ ).toMatch(`;`)
+ })
+
+ it('should handle props', async () => {
+ expect(
+ await jsx(`import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react';
+ React__default.createElement(foo, {hi: there})`)
+ ).toBeNull()
+ expect(
+ await jsx(`import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react';
+ React__default.createElement("h1", {hi: there})`)
+ ).toMatch(`;`)
+ expect(
+ await jsx(`import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react';
+ React__default.createElement(Foo, {hi: there})`)
+ ).toMatch(`;`)
+ })
+})
diff --git a/packages/plugin-react/src/jsx-runtime/restore-jsx.ts b/packages/plugin-react/src/jsx-runtime/restore-jsx.ts
index 268153f4ba5d45..dbc2218d2343ff 100644
--- a/packages/plugin-react/src/jsx-runtime/restore-jsx.ts
+++ b/packages/plugin-react/src/jsx-runtime/restore-jsx.ts
@@ -20,32 +20,42 @@ export async function restoreJSX(
}
const [reactAlias, isCommonJS] = parseReactAlias(code)
+
if (!reactAlias) {
return jsxNotFound
}
- const reactJsxRE = new RegExp(
- '\\b' + reactAlias + '\\.(createElement|Fragment)\\b',
- 'g'
- )
-
let hasCompiledJsx = false
- code = code.replace(reactJsxRE, (_, prop) => {
- hasCompiledJsx = true
- // Replace with "React" so JSX can be reverse compiled.
- return 'React.' + prop
- })
+
+ 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)
+ )
+ })
if (!hasCompiledJsx) {
return jsxNotFound
}
- // Support modules that use `import {Fragment} from 'react'`
- code = code.replace(
- /createElement\(Fragment,/g,
- 'createElement(React.Fragment,'
- )
-
babelRestoreJSX ||= import('./babel-restore-jsx')
const result = await babel.transformAsync(code, {