From 23094c7b526d9baa83f816a31436e76bdbc2da81 Mon Sep 17 00:00:00 2001 From: dimitribarbot Date: Fri, 23 May 2025 18:45:24 +0200 Subject: [PATCH 1/4] Add support for callable template literals --- src/index.ts | 26 ++++++++++++++++++++++++++ tests/fixtures/custom-jsx/index.jsx | 8 ++++++++ tests/fixtures/custom-jsx/output.jsx | 8 ++++++++ 3 files changed, 42 insertions(+) diff --git a/src/index.ts b/src/index.ts index 7e4c647..b1cd93b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -596,6 +596,19 @@ function isSortableTemplateExpression( } } + if (node.tag.type === 'CallExpression') { + let expr = node.tag.callee + + // If the tag is a CallExpression we should traverse all CallExpression's until we find the leading Identifier + while (expr.type === 'CallExpression') { + expr = expr.callee + } + + if (expr.type === 'Identifier') { + return functions.has(expr.name) + } + } + return false } @@ -626,6 +639,19 @@ function isSortableCallExpression( } } + if (node.callee.type === 'CallExpression') { + let expr = node.callee.callee + + // If the tag is a CallExpression we should traverse all CallExpression's until we find the leading Identifier + while (expr.type === 'CallExpression') { + expr = expr.callee + } + + if (expr.type === 'Identifier') { + return functions.has(expr.name) + } + } + return false } diff --git a/tests/fixtures/custom-jsx/index.jsx b/tests/fixtures/custom-jsx/index.jsx index 2a423f7..694c121 100644 --- a/tests/fixtures/custom-jsx/index.jsx +++ b/tests/fixtures/custom-jsx/index.jsx @@ -14,6 +14,14 @@ const k = tw.foo('sm:p-1 p-2'); const l = tw.foo.bar('sm:p-1 p-2'); const m = no.foo('sm:p-1 p-2'); const n = no.tw('sm:p-1 p-2'); +const o = tw(Foo)`sm:p-1 p-2`; +const p = tw(Foo)(Bar)`sm:p-1 p-2`; +const q = no(Foo)`sm:p-1 p-2`; +const r = no.tw(Foo)`sm:p-1 p-2`; +const s = tw(Foo)('sm:p-1 p-2'); +const t = tw(Foo)(Bar)('sm:p-1 p-2'); +const u = no(Foo)('sm:p-1 p-2'); +const v = no.tw(Foo)('sm:p-1 p-2'); const A = (props) =>
; const B = () => ; diff --git a/tests/fixtures/custom-jsx/output.jsx b/tests/fixtures/custom-jsx/output.jsx index 956a7aa..990e808 100644 --- a/tests/fixtures/custom-jsx/output.jsx +++ b/tests/fixtures/custom-jsx/output.jsx @@ -14,6 +14,14 @@ const k = tw.foo("p-2 sm:p-1"); const l = tw.foo.bar("p-2 sm:p-1"); const m = no.foo("sm:p-1 p-2"); const n = no.tw("sm:p-1 p-2"); +const o = tw(Foo)`p-2 sm:p-1`; +const p = tw(Foo)(Bar)`p-2 sm:p-1`; +const q = no(Foo)`sm:p-1 p-2`; +const r = no.tw(Foo)`sm:p-1 p-2`; +const s = tw(Foo)("p-2 sm:p-1"); +const t = tw(Foo)(Bar)("p-2 sm:p-1"); +const u = no(Foo)("sm:p-1 p-2"); +const v = no.tw(Foo)("sm:p-1 p-2"); const A = (props) =>
; const B = () => ; From 9b5ca27351c94ac48b91919586a2f0291354be01 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Fri, 23 May 2025 18:39:10 -0400 Subject: [PATCH 2/4] Add tests --- tests/fixtures/custom-jsx/index.jsx | 4 ++++ tests/fixtures/custom-jsx/output.jsx | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/tests/fixtures/custom-jsx/index.jsx b/tests/fixtures/custom-jsx/index.jsx index 694c121..64bae51 100644 --- a/tests/fixtures/custom-jsx/index.jsx +++ b/tests/fixtures/custom-jsx/index.jsx @@ -22,6 +22,10 @@ const s = tw(Foo)('sm:p-1 p-2'); const t = tw(Foo)(Bar)('sm:p-1 p-2'); const u = no(Foo)('sm:p-1 p-2'); const v = no.tw(Foo)('sm:p-1 p-2'); +const w = tw.div(Foo)`sm:p-1 p-2`; +const x = tw(Foo).div`sm:p-1 p-2`; +const y = no.tw(Foo)`sm:p-1 p-2`; +const z = no(Foo).tw`sm:p-1 p-2`; const A = (props) =>
; const B = () => ; diff --git a/tests/fixtures/custom-jsx/output.jsx b/tests/fixtures/custom-jsx/output.jsx index 990e808..4147daa 100644 --- a/tests/fixtures/custom-jsx/output.jsx +++ b/tests/fixtures/custom-jsx/output.jsx @@ -22,6 +22,10 @@ const s = tw(Foo)("p-2 sm:p-1"); const t = tw(Foo)(Bar)("p-2 sm:p-1"); const u = no(Foo)("sm:p-1 p-2"); const v = no.tw(Foo)("sm:p-1 p-2"); +const w = tw.div(Foo)`p-2 sm:p-1`; +const x = tw(Foo).div`p-2 sm:p-1`; +const y = no.tw(Foo)`sm:p-1 p-2`; +const z = no(Foo).tw`sm:p-1 p-2`; const A = (props) =>
; const B = () => ; From 9a17da54abbfae1fa7306cc0f689133236da638a Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Fri, 23 May 2025 18:42:39 -0400 Subject: [PATCH 3/4] Traverse mixed call and member expressions --- src/index.ts | 77 +++++++++++++--------------------------------------- 1 file changed, 19 insertions(+), 58 deletions(-) diff --git a/src/index.ts b/src/index.ts index b1cd93b..4200273 100644 --- a/src/index.ts +++ b/src/index.ts @@ -579,37 +579,7 @@ function isSortableTemplateExpression( | import('ast-types').namedTypes.TaggedTemplateExpression, functions: Set, ): boolean { - if (node.tag.type === 'Identifier') { - return functions.has(node.tag.name) - } - - if (node.tag.type === 'MemberExpression') { - let expr = node.tag.object - - // If the tag is a MemberExpression we should traverse all MemberExpression's until we find the leading Identifier - while (expr.type === 'MemberExpression') { - expr = expr.object - } - - if (expr.type === 'Identifier') { - return functions.has(expr.name) - } - } - - if (node.tag.type === 'CallExpression') { - let expr = node.tag.callee - - // If the tag is a CallExpression we should traverse all CallExpression's until we find the leading Identifier - while (expr.type === 'CallExpression') { - expr = expr.callee - } - - if (expr.type === 'Identifier') { - return functions.has(expr.name) - } - } - - return false + return isSortableExpression(node.tag, functions) } function isSortableCallExpression( @@ -618,38 +588,29 @@ function isSortableCallExpression( | import('ast-types').namedTypes.CallExpression, functions: Set, ): boolean { - if (!node.arguments?.length) { - return false - } - - if (node.callee.type === 'Identifier') { - return functions.has(node.callee.name) - } + if (!node.arguments?.length) return false - if (node.callee.type === 'MemberExpression') { - let expr = node.callee.object - - // If the tag is a MemberExpression we should traverse all MemberExpression's until we find the leading Identifier - while (expr.type === 'MemberExpression') { - expr = expr.object - } + return isSortableExpression(node.callee, functions) +} - if (expr.type === 'Identifier') { - return functions.has(expr.name) +function isSortableExpression( + node: + | import('@babel/types').Expression + | import('@babel/types').V8IntrinsicIdentifier + | import('ast-types').namedTypes.ASTNode, + functions: Set, +): boolean { + // Traverse property accesses and function calls to find the leading ident + while (node.type === 'CallExpression' || node.type === 'MemberExpression') { + if (node.type === 'CallExpression') { + node = node.callee + } else if (node.type === 'MemberExpression') { + node = node.object } } - if (node.callee.type === 'CallExpression') { - let expr = node.callee.callee - - // If the tag is a CallExpression we should traverse all CallExpression's until we find the leading Identifier - while (expr.type === 'CallExpression') { - expr = expr.callee - } - - if (expr.type === 'Identifier') { - return functions.has(expr.name) - } + if (node.type === 'Identifier') { + return functions.has(node.name) } return false From 19765a20f3b6c19af503f20e46e935eb6d6ef1ae Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Fri, 23 May 2025 18:49:18 -0400 Subject: [PATCH 4/4] Update changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index af53ffa..2bb76dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -- Nothing yet! +- Support sorting in callable template literals ([#367](https://github.com/tailwindlabs/prettier-plugin-tailwindcss/pull/367)) +- Support sorting in function calls mixed with property accesses ([#367](https://github.com/tailwindlabs/prettier-plugin-tailwindcss/pull/367)) ## [0.6.11] - 2025-01-23