diff --git a/.changeset/gold-vans-yell.md b/.changeset/gold-vans-yell.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/gold-vans-yell.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/packages/tailwindcss-transformer/src/__tests__/__snapshots__/transform-classname-cn.test.ts.snap b/packages/tailwindcss-transformer/src/__tests__/__snapshots__/transform-classname-cn.test.ts.snap index 7bad367dde7..b877c0e49ad 100644 --- a/packages/tailwindcss-transformer/src/__tests__/__snapshots__/transform-classname-cn.test.ts.snap +++ b/packages/tailwindcss-transformer/src/__tests__/__snapshots__/transform-classname-cn.test.ts.snap @@ -1,7 +1,15 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`className with cn 1`] = ` -"const Button: React.FC = ({ children, className }) => { - return ; - }" +exports[`className with cn/cx/clsx 1`] = ` +"const ButtonCn: React.FC = ({ children, className }) => { + return ; + } + + const ButtonCx: React.FC = ({ children, className }) => { + return ; + } + + const ButtonClsx: React.FC = ({ children, className }) => { + return ; + }" `; diff --git a/packages/tailwindcss-transformer/src/__tests__/__snapshots__/transform-cn-object.test.ts.snap b/packages/tailwindcss-transformer/src/__tests__/__snapshots__/transform-cn-object.test.ts.snap index f1690cc9976..debe43a6d35 100644 --- a/packages/tailwindcss-transformer/src/__tests__/__snapshots__/transform-cn-object.test.ts.snap +++ b/packages/tailwindcss-transformer/src/__tests__/__snapshots__/transform-cn-object.test.ts.snap @@ -1,7 +1,15 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`className with cn 1`] = ` -"const Button: React.FC = ({ children, intent, className }) => { - return ; - }" +exports[`className with cn/cx/clsx 1`] = ` +"const ButtonCn: React.FC = ({ children, intent, className }) => { + return ; + } + + const ButtonCx: React.FC = ({ children, intent, className }) => { + return ; + } + + const ButtonClsx: React.FC = ({ children, intent, className }) => { + return ; + }" `; diff --git a/packages/tailwindcss-transformer/src/__tests__/__snapshots__/transform-cn.test.ts.snap b/packages/tailwindcss-transformer/src/__tests__/__snapshots__/transform-cn.test.ts.snap index 9627193fdb8..2dfee0328cc 100644 --- a/packages/tailwindcss-transformer/src/__tests__/__snapshots__/transform-cn.test.ts.snap +++ b/packages/tailwindcss-transformer/src/__tests__/__snapshots__/transform-cn.test.ts.snap @@ -1,13 +1,33 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`cn 1`] = ` -"const Button: React.FC = ({ children, className }) => { - const buttonClasses = cn( - (something === 'flex' || 'flex') && "cl-222f930b", - ["cl-222f930b", "cl-222f930b"], - something === 'flex' || something === 'flex' ? (something ? "cl-222f930b" : "cl-222f930b") : "cl-222f930b", - className, - ); - return ; - };" +exports[`cn/cx/clsx 1`] = ` +"const ButtonCn: React.FC = ({ children, className }) => { + const buttonClasses = cn( + (something === 'flex' || 'flex') && "cl-222f930b", + ["cl-222f930b", "cl-222f930b"], + something === 'flex' || something === 'flex' ? (something ? "cl-222f930b" : "cl-222f930b") : "cl-222f930b", + className, + ); + return ; + }; + + const ButtonCx: React.FC = ({ children, className }) => { + const buttonClasses = cx( + (something === 'flex' || 'flex') && "cl-222f930b", + ["cl-222f930b", "cl-222f930b"], + something === 'flex' || something === 'flex' ? (something ? "cl-222f930b" : "cl-222f930b") : "cl-222f930b", + className, + ); + return ; + }; + + const ButtonClsx: React.FC = ({ children, className }) => { + const buttonClasses = clsx( + (something === 'flex' || 'flex') && "cl-222f930b", + ["cl-222f930b", "cl-222f930b"], + something === 'flex' || something === 'flex' ? (something ? "cl-222f930b" : "cl-222f930b") : "cl-222f930b", + className, + ); + return ; + };" `; diff --git a/packages/tailwindcss-transformer/src/__tests__/__snapshots__/transform-cva.test.ts.snap b/packages/tailwindcss-transformer/src/__tests__/__snapshots__/transform-cva.test.ts.snap new file mode 100644 index 00000000000..ab2ee9203ce --- /dev/null +++ b/packages/tailwindcss-transformer/src/__tests__/__snapshots__/transform-cva.test.ts.snap @@ -0,0 +1,41 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`cva 1`] = ` +"const button = cva({ + base: "cl-7e77acd4", + variants: { + intent: { + primary: [ + "cl-44eeb93b", + "cl-5e9a40ae", + "cl-e9c0d2f3", + "cl-d8b3f644", + ], + secondary: "cl-6537e500", + }, + size: { + small: "cl-716a7c80", + medium: "cl-3f348faf", + }, + }, + compoundVariants: [ + { + intent: "primary", + size: "medium", + className: "cl-d2cf63c7" + }, + ], + defaultVariants: { + intent: "primary", + size: "medium", + }, + }); + + const Button: React.FC = ({ children, intent, size, className }) => { + return ; + } + + const ButtonLink: React.FC = ({ children, intent, size, className }) => { + return {children} + }" +`; diff --git a/packages/tailwindcss-transformer/src/__tests__/transform-classname-cn.test.ts b/packages/tailwindcss-transformer/src/__tests__/transform-classname-cn.test.ts index 9dc07794c46..590cda41933 100644 --- a/packages/tailwindcss-transformer/src/__tests__/transform-classname-cn.test.ts +++ b/packages/tailwindcss-transformer/src/__tests__/transform-classname-cn.test.ts @@ -1,10 +1,18 @@ import { transform } from '../index'; -test('className with cn', () => { +test('className with cn/cx/clsx', () => { const result = transform( - `const Button: React.FC = ({ children, className }) => { - return - }`, + `const ButtonCn: React.FC = ({ children, className }) => { + return + } + + const ButtonCx: React.FC = ({ children, className }) => { + return + } + + const ButtonClsx: React.FC = ({ children, className }) => { + return + }`, { styleCache: new Map(), }, diff --git a/packages/tailwindcss-transformer/src/__tests__/transform-cn-object.test.ts b/packages/tailwindcss-transformer/src/__tests__/transform-cn-object.test.ts index c29db0179e3..482946684ae 100644 --- a/packages/tailwindcss-transformer/src/__tests__/transform-cn-object.test.ts +++ b/packages/tailwindcss-transformer/src/__tests__/transform-cn-object.test.ts @@ -1,10 +1,18 @@ import { transform } from '../index'; -test('className with cn', () => { +test('className with cn/cx/clsx', () => { const result = transform( - `const Button: React.FC = ({ children, intent, className }) => { - return - }`, + `const ButtonCn: React.FC = ({ children, intent, className }) => { + return + } + + const ButtonCx: React.FC = ({ children, intent, className }) => { + return + } + + const ButtonClsx: React.FC = ({ children, intent, className }) => { + return + }`, { styleCache: new Map(), }, diff --git a/packages/tailwindcss-transformer/src/__tests__/transform-cn.test.ts b/packages/tailwindcss-transformer/src/__tests__/transform-cn.test.ts index 6aef8a6c75b..4cfac3815ca 100644 --- a/packages/tailwindcss-transformer/src/__tests__/transform-cn.test.ts +++ b/packages/tailwindcss-transformer/src/__tests__/transform-cn.test.ts @@ -1,16 +1,36 @@ import { transform } from '../index'; -test('cn', () => { +test('cn/cx/clsx', () => { const result = transform( - `const Button: React.FC = ({ children, className }) => { - const buttonClasses = cn( - (something === 'flex' || 'flex') && 'flex', - ['flex', 'flex'], - something === 'flex' || something === 'flex' ? (something ? 'flex' : 'flex') : 'flex', - className, - ); - return ; - };`, + `const ButtonCn: React.FC = ({ children, className }) => { + const buttonClasses = cn( + (something === 'flex' || 'flex') && 'flex', + ['flex', 'flex'], + something === 'flex' || something === 'flex' ? (something ? 'flex' : 'flex') : 'flex', + className, + ); + return ; + }; + + const ButtonCx: React.FC = ({ children, className }) => { + const buttonClasses = cx( + (something === 'flex' || 'flex') && 'flex', + ['flex', 'flex'], + something === 'flex' || something === 'flex' ? (something ? 'flex' : 'flex') : 'flex', + className, + ); + return ; + }; + + const ButtonClsx: React.FC = ({ children, className }) => { + const buttonClasses = clsx( + (something === 'flex' || 'flex') && 'flex', + ['flex', 'flex'], + something === 'flex' || something === 'flex' ? (something ? 'flex' : 'flex') : 'flex', + className, + ); + return ; + };`, { styleCache: new Map(), }, diff --git a/packages/tailwindcss-transformer/src/__tests__/transform-cva.test.ts b/packages/tailwindcss-transformer/src/__tests__/transform-cva.test.ts new file mode 100644 index 00000000000..d92aa04db66 --- /dev/null +++ b/packages/tailwindcss-transformer/src/__tests__/transform-cva.test.ts @@ -0,0 +1,47 @@ +import { transform } from '../index'; + +test('cva', () => { + const result = transform( + `const button = cva({ + base: "rounded border font-semibold", + variants: { + intent: { + primary: [ + "bg-blue-500", + "text-white", + "border-transparent", + "hover:bg-blue-600", + ], + secondary: "border-gray-400 bg-white text-gray-800 hover:bg-gray-100", + }, + size: { + small: "px-2 py-1 text-sm", + medium: "px-4 py-2 text-base", + }, + }, + compoundVariants: [ + { + intent: "primary", + size: "medium", + className: "uppercase" + }, + ], + defaultVariants: { + intent: "primary", + size: "medium", + }, + }); + + const Button: React.FC = ({ children, intent, size, className }) => { + return + } + + const ButtonLink: React.FC = ({ children, intent, size, className }) => { + return {children} + }`, + { + styleCache: new Map(), + }, + ); + expect(result).toMatchSnapshot(); +}); diff --git a/packages/tailwindcss-transformer/src/index.ts b/packages/tailwindcss-transformer/src/index.ts index c86b278a913..b2b20343542 100644 --- a/packages/tailwindcss-transformer/src/index.ts +++ b/packages/tailwindcss-transformer/src/index.ts @@ -73,12 +73,37 @@ export function transform(code: string, ctx: { styleCache: StyleCache }) { } this.traverse(path); }, - // visit cn function calls containing TW classes + // visit a `className` property within any object containing TW classes + visitObjectProperty(path) { + const node = path.node; + if (path.node.key.type === 'Identifier' && path.node.key.name === 'className') { + visitNode(node, ctx); + } + this.traverse(path); + }, + // visit function calls containing TW classes visitCallExpression(path) { const node = path.node; - if (node.callee.type === 'Identifier' && node.callee.name === 'cn') { + // `className` concatenation functions + if (node.callee.type === 'Identifier' && ['cn', 'cx', 'clsx'].includes(node.callee.name)) { visitNode(node, ctx); } + // cva functions (note: only compatible with cva@1.0) + if ( + node.callee.type === 'Identifier' && + node.callee.name === 'cva' && + node.arguments[0]?.type === 'ObjectExpression' + ) { + for (const property of node.arguments[0].properties) { + if ( + property.type === 'ObjectProperty' && + property.key.type === 'Identifier' && + ['base', 'variants'].includes(property.key.name) + ) { + visitNode(property, ctx); + } + } + } this.traverse(path); }, });