diff --git a/packages/unminify/src/transformations/__tests__/un-jsx.spec.ts b/packages/unminify/src/transformations/__tests__/un-jsx.spec.ts index 5a3dd52b..194e86aa 100644 --- a/packages/unminify/src/transformations/__tests__/un-jsx.spec.ts +++ b/packages/unminify/src/transformations/__tests__/un-jsx.spec.ts @@ -206,6 +206,35 @@ const Foo = () => { `, ) +inlineTest('jsx with dynamic Component tag - tag name in constant variable', + ` +function fn() { + const Name = "div"; + return React.createElement(Name, null); +} +`, + ` +function fn() { + return
; +} +`, +) + +inlineTest('jsx with dynamic Component tag - tag name in constant variable but the value is not a string', + ` +function fn() { + const Name = 1; + return React.createElement(Name, null); +} +`, + ` +function fn() { + const Name = 1; + return ; +} +`, +) + inlineTest('jsx with child text that should be wrapped', ` function fn() { diff --git a/packages/unminify/src/transformations/un-jsx.ts b/packages/unminify/src/transformations/un-jsx.ts index 9abc5b27..ddecf690 100644 --- a/packages/unminify/src/transformations/un-jsx.ts +++ b/packages/unminify/src/transformations/un-jsx.ts @@ -4,6 +4,7 @@ import { removePureAnnotation } from '@wakaru/ast-utils/comments' import { generateName } from '@wakaru/ast-utils/identifier' import { insertBefore } from '@wakaru/ast-utils/insert' import { isNull, isTrue, isUndefined } from '@wakaru/ast-utils/matchers' +import { findDeclaration, removeDeclarationIfUnused } from '@wakaru/ast-utils/scope' import { nonNullable } from '@wakaru/shared/array' import { createJSCodeshiftTransformationRule } from '@wakaru/shared/rule' import { z } from 'zod' @@ -106,7 +107,6 @@ export const transformAST: ASTTransformation = (context, params) if (jsxElement) { const parentWithComments = j.ExpressionStatement.check(path.parent.node) ? path.parent : path removePureAnnotation(j, parentWithComments.node) - path.replace(jsxElement) } }) @@ -131,6 +131,27 @@ function toJSX(j: JSCodeshift, path: ASTPath, pragmas: string[], if (isCapitalizationInvalid(j, type)) return null let tag = toJsxTag(j, type) + + // constant tag name will convert into JSX tag + if (j.JSXIdentifier.check(tag)) { + const scope = path.scope + assertScopeExists(scope) + + const tagName = tag.name + const declaration = findDeclaration(scope, tagName) + if (declaration) { + // if the tag is a variable and it's string literal, inline it + const variableDeclarator = j(declaration).closest(j.VariableDeclarator) + if (variableDeclarator.size() === 1) { + const init = variableDeclarator.get().node.init + if (j.StringLiteral.check(init)) { + tag = j.jsxIdentifier(init.value) + removeDeclarationIfUnused(j, path, tagName) + } + } + } + } + // If a tag cannot be converted to JSX tag, convert it to a variable if (!tag && !j.SpreadElement.check(type)) { const name = generateName('Component', scope)