Skip to content

Commit

Permalink
feat: support dynamic jsx tag
Browse files Browse the repository at this point in the history
  • Loading branch information
pionxzh committed Dec 30, 2023
1 parent 332c84b commit 0b51e23
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 6 deletions.
56 changes: 56 additions & 0 deletions packages/unminify/src/transformations/__tests__/un-jsx.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,62 @@ function fn() {
`,
)

inlineTest('jsx with dynamic Component tag',
`
function fn() {
return React.createElement(
r ? "a" : "div",
null,
"Hello",
);
}
`,
`
function fn() {
const Component = r ? "a" : "div";
return <Component>Hello</Component>;
}
`,
)

inlineTest('jsx with dynamic Component tag #2',
`
function fn() {
return React.createElement(
components[0],
null,
"Hello",
);
}
`,
`
function fn() {
const Component = components[0];
return <Component>Hello</Component>;
}
`,
)

inlineTest('jsx with dynamic Component tag #3',
`
const Foo = () => {
return jsxs("div", {
children: [
jsx(r ? "a" : "div", { children: "bar" }, "b"),
jsx(g ? "p" : "div", { children: "baz" }, c),
]
});
};
`,
`
const Foo = () => {
const Component = g ? "p" : "div";
const Component$0 = r ? "a" : "div";
return <div><Component$0 key="b">bar</Component$0><Component key={c}>baz</Component></div>;
};
`,
)

inlineTest('jsx with child text that should be wrapped',
`
function fn() {
Expand Down
31 changes: 25 additions & 6 deletions packages/unminify/src/transformations/un-jsx.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { assertScopeExists } from '@wakaru/ast-utils/assert'
import { pascalCase } from '@wakaru/ast-utils/case'
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 { wrapAstTransformation } from '@wakaru/ast-utils/wrapAstTransformation'
import { z } from 'zod'
import { nonNullable } from '../utils/utils'
import type { ASTTransformation } from '@wakaru/ast-utils/wrapAstTransformation'
import type { ExpressionKind, LiteralKind } from 'ast-types/lib/gen/kinds'
import type { ASTNode, CallExpression, Collection, Identifier, JSCodeshift, JSXAttribute, JSXElement, JSXExpressionContainer, JSXFragment, JSXIdentifier, JSXMemberExpression, JSXSpreadAttribute, JSXSpreadChild, JSXText, MemberExpression, RestElement, SpreadElement, StringLiteral, VariableDeclarator } from 'jscodeshift'
import type { ASTNode, ASTPath, CallExpression, Collection, Identifier, JSCodeshift, JSXAttribute, JSXElement, JSXExpressionContainer, JSXFragment, JSXIdentifier, JSXMemberExpression, JSXSpreadAttribute, JSXSpreadChild, JSXText, MemberExpression, RestElement, SpreadElement, StringLiteral, VariableDeclarator } from 'jscodeshift'

export const Schema = z.object({
pragma: z.string().optional().describe('The pragma to use for JSX transformation.'),
Expand Down Expand Up @@ -100,7 +102,7 @@ export const transformAST: ASTTransformation<Params> = (context, params) => {
// bottom-up transformation
.reverse()
.forEach((path) => {
const jsxElement = toJSX(j, path.node, pragmas, pragmaFrags)
const jsxElement = toJSX(j, path, pragmas, pragmaFrags)
if (jsxElement) {
const parentWithComments = j.ExpressionStatement.check(path.parent.node) ? path.parent : path
removePureAnnotation(j, parentWithComments.node)
Expand All @@ -110,7 +112,11 @@ export const transformAST: ASTTransformation<Params> = (context, params) => {
})
}

function toJSX(j: JSCodeshift, node: CallExpression, pragmas: string[], pragmaFrags: string[]): JSXElement | JSXFragment | null {
function toJSX(j: JSCodeshift, path: ASTPath<CallExpression>, pragmas: string[], pragmaFrags: string[]): JSXElement | JSXFragment | null {
const scope = path.scope
assertScopeExists(scope)

const node = path.node
const pragma = getPragma(j, node.callee, pragmas)
if (!pragma) return null

Expand All @@ -124,7 +130,16 @@ function toJSX(j: JSCodeshift, node: CallExpression, pragmas: string[], pragmaFr

if (isCapitalizationInvalid(j, type)) return null

const tag = toJsxTag(j, type)
let tag = toJsxTag(j, type)
// 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)
tag = j.jsxIdentifier(name)

const variableDeclaration = j.variableDeclaration('const', [j.variableDeclarator(j.identifier(name), type)])
insertBefore(j, path, variableDeclaration)
scope.markAsStale()
}
if (!tag) return null

const attributes = toJsxAttributes(j, props)
Expand Down Expand Up @@ -204,9 +219,13 @@ function toJsxTag(j: JSCodeshift, node: SpreadElement | ExpressionKind): JSXIden
return j.jsxIdentifier(node.name)
}
else if (j.MemberExpression.check(node)) {
const object = toJsxTag(j, node.object)
const property = toJsxTag(j, node.property)
if (!object || !property) return null

return j.jsxMemberExpression(
toJsxTag(j, node.object) as JSXIdentifier | JSXMemberExpression,
toJsxTag(j, node.property) as JSXIdentifier,
object as JSXIdentifier | JSXMemberExpression,
property as JSXIdentifier,
)
}

Expand Down

0 comments on commit 0b51e23

Please sign in to comment.