Fix wrapper

This commit is contained in:
Alex 2025-02-10 02:29:07 -05:00
parent 8e41208a14
commit 52b19b3b36
4 changed files with 70 additions and 62 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "@aet/tailwind", "name": "@aet/tailwind",
"version": "1.0.28", "version": "1.0.31",
"license": "MIT", "license": "MIT",
"type": "module", "type": "module",
"scripts": { "scripts": {

View File

@ -68,10 +68,29 @@ describe("merges with existing className attribute", () => {
const clsName = getClassName("text-center"); const clsName = getClassName("text-center");
expect(files.js.text).toContain( 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 (
<div className={className} css="text-center">
<span {...props}>Hello, world!</span>
</div>
);
}
`,
});
const clsName = getClassName("text-center");
expect(files.js.text).toContain( 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),`
); );
}); });

View File

@ -52,7 +52,7 @@ export function getBuild(name: string) {
external: [ external: [
"react", "react",
"react/jsx-runtime", "react/jsx-runtime",
"react-aria-component", "react-aria-components",
"@emotion/css", "@emotion/css",
"clsx", "clsx",
"tslib", "tslib",

View File

@ -53,7 +53,6 @@ function getUtils({
let styleImport: t.Identifier; let styleImport: t.Identifier;
let classedImport: t.Identifier; let classedImport: t.Identifier;
let cssModuleImport: t.Identifier; let cssModuleImport: t.Identifier;
let composeRenderPropsImport: t.Identifier;
const cssMap = new Map<string, StyleMapEntry>(); const cssMap = new Map<string, StyleMapEntry>();
const jsMap = new Map<string, StyleMapEntry>(); const jsMap = new Map<string, StyleMapEntry>();
@ -177,24 +176,6 @@ function getUtils({
return t.cloneNode(tslibImport); 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: () => { getClassedImport: () => {
if (classedImport == null) { if (classedImport == null) {
classedImport = path.scope.generateUidIdentifier("classed"); 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); let valuePathNode = extractJSXContainer(valuePath.node);
if ( if (
t.isArrayExpression(valuePathNode) && 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 // There is an existing className attribute
if (classNameAttribute) { if (classNameAttribute) {
const attrValue = classNameAttribute.value!; 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 both are string literals, we can merge them directly here
if (t.isStringLiteral(attrValue) && t.isStringLiteral(valuePathNode)) { if (t.isStringLiteral(attrValue) && t.isStringLiteral(valuePathNode)) {
@ -398,31 +408,17 @@ export function babelTailwind(
?.referencePaths.map(p => p.node) ?.referencePaths.map(p => p.node)
.includes(internal.callee) .includes(internal.callee)
) { ) {
classNameAttribute.value = t.jsxExpressionContainer( classNameAttribute.value = jsxBox(
wrap( call(_.getCx(path.scope), [
...(internal.arguments as (b.types.Expression | b.types.SpreadElement)[]) valuePathNode,
) ...(internal.arguments as (b.types.Expression | b.types.SpreadElement)[]),
])
); );
} else { } else {
classNameAttribute.value = t.jsxExpressionContainer(wrap(internal)); classNameAttribute.value = jsxBox(wrap(internal));
} }
} }
} else { } 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)); const rest = parent.attributes.filter(attr => t.isJSXSpreadAttribute(attr));
let arg; let arg;
// if there is only one JSX spread attribute and it's an identifier // if there is only one JSX spread attribute and it's an identifier
@ -447,36 +443,29 @@ export function babelTailwind(
// (props) => ... // (props) => ...
// ↪ ({ className, ...props }) => ... // ↪ ({ className, ...props }) => ...
scope.path.parent.params[index] = t.objectPattern([ scope.path.parent.params[index] = t.objectPattern([
t.objectProperty(t.identifier("className"), clsVar), t.objectProperty(id("className"), clsVar),
t.restElement(node), t.restElement(node),
]); ]);
} else { } else {
// ({ ...props }) => ... // ({ ...props }) => ...
// ↪ ({ className, ...props }) => ... // ↪ ({ className, ...props }) => ...
node.properties.unshift( node.properties.unshift(t.objectProperty(id("className"), clsVar));
t.objectProperty(t.identifier("className"), clsVar)
);
} }
parent.attributes.push( parent.attributes.push(
t.jsxAttribute( t.jsxAttribute(jsxId("className"), jsxBox(wrap(clsVar)))
t.jsxIdentifier("className"),
t.jsxExpressionContainer(wrap(clsVar))
)
); );
} else { } else {
const tslibImport = _.getTSlibImport(); const tslibImport = _.getTSlibImport();
rest[0].argument = t.callExpression( rest[0].argument = call(t.memberExpression(tslibImport, id("__rest")), [
t.memberExpression(tslibImport, t.identifier("__rest")), arg,
[arg, t.arrayExpression([t.stringLiteral("className")])] t.arrayExpression([t.stringLiteral("className")]),
); ]);
parent.attributes.push( parent.attributes.push(
t.jsxAttribute( t.jsxAttribute(
t.jsxIdentifier("className"), jsxId("className"),
t.jsxExpressionContainer( jsxBox(wrap(t.memberExpression(arg, id("className"))))
wrap(t.memberExpression(arg, t.identifier("className")))
)
) )
); );
} }
@ -484,7 +473,7 @@ export function babelTailwind(
// Fallback // Fallback
const containerValue = t.isStringLiteral(valuePathNode) const containerValue = t.isStringLiteral(valuePathNode)
? valuePathNode ? valuePathNode
: t.callExpression(_.getCx(path.scope), [valuePathNode]); : call(_.getCx(path.scope), [valuePathNode]);
parent.attributes.push( parent.attributes.push(
t.jsxAttribute( t.jsxAttribute(