Skip to content

Commit db0bd74

Browse files
Add support for callable template literals when using custom JSX parser (#367)
* Add support for callable template literals * Add tests * Traverse mixed call and member expressions * Update changelog --------- Co-authored-by: Jordan Pittman <[email protected]>
1 parent 4794bce commit db0bd74

File tree

4 files changed

+46
-33
lines changed

4 files changed

+46
-33
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
- Don't augment global Prettier `ParserOptions` and `RequiredOptions` types ([#354](https://github.com/tailwindlabs/prettier-plugin-tailwindcss/pull/354))
1111
- Drop support for `prettier-plugin-import-sort` ([#385](https://github.com/tailwindlabs/prettier-plugin-tailwindcss/pull/385))
1212
- Format quotes in `@source`, `@plugin`, and `@config` ([#387](https://github.com/tailwindlabs/prettier-plugin-tailwindcss/pull/387))
13+
- Support sorting in callable template literals ([#367](https://github.com/tailwindlabs/prettier-plugin-tailwindcss/pull/367))
14+
- Support sorting in function calls mixed with property accesses ([#367](https://github.com/tailwindlabs/prettier-plugin-tailwindcss/pull/367))
1315

1416
## [0.6.14] - 2025-07-09
1517

src/index.ts

Lines changed: 20 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -624,24 +624,7 @@ function isSortableTemplateExpression(
624624
| import('ast-types').namedTypes.TaggedTemplateExpression,
625625
functions: Set<string>,
626626
): boolean {
627-
if (node.tag.type === 'Identifier') {
628-
return functions.has(node.tag.name)
629-
}
630-
631-
if (node.tag.type === 'MemberExpression') {
632-
let expr = node.tag.object
633-
634-
// If the tag is a MemberExpression we should traverse all MemberExpression's until we find the leading Identifier
635-
while (expr.type === 'MemberExpression') {
636-
expr = expr.object
637-
}
638-
639-
if (expr.type === 'Identifier') {
640-
return functions.has(expr.name)
641-
}
642-
}
643-
644-
return false
627+
return isSortableExpression(node.tag, functions)
645628
}
646629

647630
function isSortableCallExpression(
@@ -650,25 +633,29 @@ function isSortableCallExpression(
650633
| import('ast-types').namedTypes.CallExpression,
651634
functions: Set<string>,
652635
): boolean {
653-
if (!node.arguments?.length) {
654-
return false
655-
}
656-
657-
if (node.callee.type === 'Identifier') {
658-
return functions.has(node.callee.name)
659-
}
636+
if (!node.arguments?.length) return false
660637

661-
if (node.callee.type === 'MemberExpression') {
662-
let expr = node.callee.object
638+
return isSortableExpression(node.callee, functions)
639+
}
663640

664-
// If the tag is a MemberExpression we should traverse all MemberExpression's until we find the leading Identifier
665-
while (expr.type === 'MemberExpression') {
666-
expr = expr.object
641+
function isSortableExpression(
642+
node:
643+
| import('@babel/types').Expression
644+
| import('@babel/types').V8IntrinsicIdentifier
645+
| import('ast-types').namedTypes.ASTNode,
646+
functions: Set<string>,
647+
): boolean {
648+
// Traverse property accesses and function calls to find the leading ident
649+
while (node.type === 'CallExpression' || node.type === 'MemberExpression') {
650+
if (node.type === 'CallExpression') {
651+
node = node.callee
652+
} else if (node.type === 'MemberExpression') {
653+
node = node.object
667654
}
655+
}
668656

669-
if (expr.type === 'Identifier') {
670-
return functions.has(expr.name)
671-
}
657+
if (node.type === 'Identifier') {
658+
return functions.has(node.name)
672659
}
673660

674661
return false

tests/fixtures/custom-jsx/index.jsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,18 @@ const k = tw.foo('sm:p-1 p-2');
1414
const l = tw.foo.bar('sm:p-1 p-2');
1515
const m = no.foo('sm:p-1 p-2');
1616
const n = no.tw('sm:p-1 p-2');
17+
const o = tw(Foo)`sm:p-1 p-2`;
18+
const p = tw(Foo)(Bar)`sm:p-1 p-2`;
19+
const q = no(Foo)`sm:p-1 p-2`;
20+
const r = no.tw(Foo)`sm:p-1 p-2`;
21+
const s = tw(Foo)('sm:p-1 p-2');
22+
const t = tw(Foo)(Bar)('sm:p-1 p-2');
23+
const u = no(Foo)('sm:p-1 p-2');
24+
const v = no.tw(Foo)('sm:p-1 p-2');
25+
const w = tw.div(Foo)`sm:p-1 p-2`;
26+
const x = tw(Foo).div`sm:p-1 p-2`;
27+
const y = no.tw(Foo)`sm:p-1 p-2`;
28+
const z = no(Foo).tw`sm:p-1 p-2`;
1729

1830
const A = (props) => <div className={props.sortMe} />;
1931
const B = () => <A sortMe="sm:p-1 p-2" dontSort="sm:p-1 p-2" />;

tests/fixtures/custom-jsx/output.jsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,18 @@ const k = tw.foo("p-2 sm:p-1");
1414
const l = tw.foo.bar("p-2 sm:p-1");
1515
const m = no.foo("sm:p-1 p-2");
1616
const n = no.tw("sm:p-1 p-2");
17+
const o = tw(Foo)`p-2 sm:p-1`;
18+
const p = tw(Foo)(Bar)`p-2 sm:p-1`;
19+
const q = no(Foo)`sm:p-1 p-2`;
20+
const r = no.tw(Foo)`sm:p-1 p-2`;
21+
const s = tw(Foo)("p-2 sm:p-1");
22+
const t = tw(Foo)(Bar)("p-2 sm:p-1");
23+
const u = no(Foo)("sm:p-1 p-2");
24+
const v = no.tw(Foo)("sm:p-1 p-2");
25+
const w = tw.div(Foo)`p-2 sm:p-1`;
26+
const x = tw(Foo).div`p-2 sm:p-1`;
27+
const y = no.tw(Foo)`sm:p-1 p-2`;
28+
const z = no(Foo).tw`sm:p-1 p-2`;
1729

1830
const A = (props) => <div className={props.sortMe} />;
1931
const B = () => <A sortMe="p-2 sm:p-1" dontSort="sm:p-1 p-2" />;

0 commit comments

Comments
 (0)