From 52b19b3b36983a1209021aa6fea82516f7f6b0b9 Mon Sep 17 00:00:00 2001 From: Alex <8125011+alex-kinokon@users.noreply.github.com> Date: Mon, 10 Feb 2025 02:29:07 -0500 Subject: [PATCH] Fix wrapper --- package.json | 2 +- src/__tests__/merge.test.ts | 23 +++++++- src/__tests__/utils.ts | 2 +- src/babel/index.ts | 105 ++++++++++++++++-------------------- 4 files changed, 70 insertions(+), 62 deletions(-) diff --git a/package.json b/package.json index cb12805..8d04ff1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@aet/tailwind", - "version": "1.0.28", + "version": "1.0.31", "license": "MIT", "type": "module", "scripts": { diff --git a/src/__tests__/merge.test.ts b/src/__tests__/merge.test.ts index f56f0ff..ecf4dc5 100644 --- a/src/__tests__/merge.test.ts +++ b/src/__tests__/merge.test.ts @@ -68,10 +68,29 @@ describe("merges with existing className attribute", () => { const clsName = getClassName("text-center"); expect(files.js.text).toContain( - `import { composeRenderProps as _composeRenderProps } from "react-aria-component";` + `{ ...props, className: typeof _className === "function" ? (...args) => _cx("${clsName}", _className(...args)) : _cx("${clsName}", _className),` ); + }); + + it("supports composeRenderProps (2)", async () => { + const { files } = await compileESBuild({ + clsx: "clsx", + expectFiles: 2, + composeRenderProps: true, + javascript: /* tsx */ ` + export function Hello({ className, ...props }) { + return ( +
+ Hello, world! +
+ ); + } + `, + }); + + const clsName = getClassName("text-center"); expect(files.js.text).toContain( - `...props, className: _composeRenderProps(_className, (n) => _cx("${clsName}", n)),` + `{ className: typeof className === "function" ? (...args) => _cx("${clsName}", className(...args)) : _cx("${clsName}", className),` ); }); diff --git a/src/__tests__/utils.ts b/src/__tests__/utils.ts index 6365f48..db736d5 100644 --- a/src/__tests__/utils.ts +++ b/src/__tests__/utils.ts @@ -52,7 +52,7 @@ export function getBuild(name: string) { external: [ "react", "react/jsx-runtime", - "react-aria-component", + "react-aria-components", "@emotion/css", "clsx", "tslib", diff --git a/src/babel/index.ts b/src/babel/index.ts index 6519093..7cdf797 100644 --- a/src/babel/index.ts +++ b/src/babel/index.ts @@ -53,7 +53,6 @@ function getUtils({ let styleImport: t.Identifier; let classedImport: t.Identifier; let cssModuleImport: t.Identifier; - let composeRenderPropsImport: t.Identifier; const cssMap = new Map(); const jsMap = new Map(); @@ -177,24 +176,6 @@ function getUtils({ return t.cloneNode(tslibImport); }, - getComposeRenderPropsImport: () => { - if (composeRenderPropsImport == null) { - composeRenderPropsImport = path.scope.generateUidIdentifier("composeRenderProps"); - path.node.body.unshift( - t.importDeclaration( - [ - t.importSpecifier( - composeRenderPropsImport, - t.identifier("composeRenderProps") - ), - ], - t.stringLiteral("react-aria-component") - ) - ); - } - return t.cloneNode(composeRenderPropsImport); - }, - getClassedImport: () => { if (classedImport == null) { classedImport = path.scope.generateUidIdentifier("classed"); @@ -357,6 +338,16 @@ export function babelTailwind( }, })); + const $eq = (left: t.Expression, right: t.Expression) => + t.binaryExpression("===", left, right); + const $typeof = (expr: t.Expression) => t.unaryExpression("typeof", expr); + const { + identifier: id, + jsxExpressionContainer: jsxBox, + jsxIdentifier: jsxId, + callExpression: call, + } = t; + let valuePathNode = extractJSXContainer(valuePath.node); if ( t.isArrayExpression(valuePathNode) && @@ -368,11 +359,30 @@ export function babelTailwind( ); } + const wrap = (existing: b.types.Expression) => { + const callExp = call(_.getCx(path.scope), [valuePathNode, existing]); + + return composeRenderProps + ? // typeof className === "function" + // ? (...args) => clsx("${clsName}", className(...args)) + // : clsx("${clsName}", className) + t.conditionalExpression( + $eq($typeof(existing), t.stringLiteral("function")), + t.arrowFunctionExpression( + [t.restElement(id("args"))], + call(_.getCx(path.scope), [ + valuePathNode, + call(existing, [t.spreadElement(id("args"))]), + ]) + ), + /* else */ callExp + ) + : callExp; + }; + // There is an existing className attribute if (classNameAttribute) { const attrValue = classNameAttribute.value!; - const wrap = (...originalValue: (b.types.Expression | b.types.SpreadElement)[]) => - t.callExpression(_.getCx(path.scope), [valuePathNode, ...originalValue]); // If both are string literals, we can merge them directly here if (t.isStringLiteral(attrValue) && t.isStringLiteral(valuePathNode)) { @@ -398,31 +408,17 @@ export function babelTailwind( ?.referencePaths.map(p => p.node) .includes(internal.callee) ) { - classNameAttribute.value = t.jsxExpressionContainer( - wrap( - ...(internal.arguments as (b.types.Expression | b.types.SpreadElement)[]) - ) + classNameAttribute.value = jsxBox( + call(_.getCx(path.scope), [ + valuePathNode, + ...(internal.arguments as (b.types.Expression | b.types.SpreadElement)[]), + ]) ); } else { - classNameAttribute.value = t.jsxExpressionContainer(wrap(internal)); + classNameAttribute.value = jsxBox(wrap(internal)); } } } else { - const wrap = (originalValue: b.types.Expression) => - composeRenderProps - ? // composeRenderProps(className, n => cn("...", n)) - t.callExpression(_.getComposeRenderPropsImport(), [ - originalValue, - t.arrowFunctionExpression( - [t.identifier("n")], - t.callExpression(_.getCx(path.scope), [ - valuePathNode, - t.identifier("n"), - ]) - ), - ]) - : t.callExpression(_.getCx(path.scope), [valuePathNode, originalValue]); - const rest = parent.attributes.filter(attr => t.isJSXSpreadAttribute(attr)); let arg; // if there is only one JSX spread attribute and it's an identifier @@ -447,36 +443,29 @@ export function babelTailwind( // (props) => ... // ↪ ({ className, ...props }) => ... scope.path.parent.params[index] = t.objectPattern([ - t.objectProperty(t.identifier("className"), clsVar), + t.objectProperty(id("className"), clsVar), t.restElement(node), ]); } else { // ({ ...props }) => ... // ↪ ({ className, ...props }) => ... - node.properties.unshift( - t.objectProperty(t.identifier("className"), clsVar) - ); + node.properties.unshift(t.objectProperty(id("className"), clsVar)); } parent.attributes.push( - t.jsxAttribute( - t.jsxIdentifier("className"), - t.jsxExpressionContainer(wrap(clsVar)) - ) + t.jsxAttribute(jsxId("className"), jsxBox(wrap(clsVar))) ); } else { const tslibImport = _.getTSlibImport(); - rest[0].argument = t.callExpression( - t.memberExpression(tslibImport, t.identifier("__rest")), - [arg, t.arrayExpression([t.stringLiteral("className")])] - ); + rest[0].argument = call(t.memberExpression(tslibImport, id("__rest")), [ + arg, + t.arrayExpression([t.stringLiteral("className")]), + ]); parent.attributes.push( t.jsxAttribute( - t.jsxIdentifier("className"), - t.jsxExpressionContainer( - wrap(t.memberExpression(arg, t.identifier("className"))) - ) + jsxId("className"), + jsxBox(wrap(t.memberExpression(arg, id("className")))) ) ); } @@ -484,7 +473,7 @@ export function babelTailwind( // Fallback const containerValue = t.isStringLiteral(valuePathNode) ? valuePathNode - : t.callExpression(_.getCx(path.scope), [valuePathNode]); + : call(_.getCx(path.scope), [valuePathNode]); parent.attributes.push( t.jsxAttribute(