diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f4444d..7ccce0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Format quotes in `@source`, `@plugin`, and `@config` ([#387](https://github.com/tailwindlabs/prettier-plugin-tailwindcss/pull/387)) - 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)) +- Support sorting in function calls in Twig ([#358](https://github.com/tailwindlabs/prettier-plugin-tailwindcss/pull/358)) ## [0.6.14] - 2025-07-09 diff --git a/src/index.ts b/src/index.ts index 933d3ea..03a1eb1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -898,7 +898,7 @@ function transformMarko(ast: any, { env }: TransformerContext) { } function transformTwig(ast: any, { env, changes }: TransformerContext) { - let { staticAttrs } = env.customizations + let { staticAttrs, functions } = env.customizations for (let child of ast.expressions ?? []) { transformTwig(child, { env, changes }) @@ -911,6 +911,31 @@ function transformTwig(ast: any, { env, changes }: TransformerContext) { meta.sortTextNodes = true }, + CallExpression(node, _path, meta) { + // Traverse property accesses and function calls to find the *trailing* ident + while ( + node.type === 'CallExpression' || + node.type === 'MemberExpression' + ) { + if (node.type === 'CallExpression') { + node = node.callee + } else if (node.type === 'MemberExpression') { + // TODO: This is *different* than `isSortableExpression` and that doesn't feel right + // but they're mutually exclusive implementations + // + // This is to handle foo.fnNameHere(…) where `isSortableExpression` is intentionally + // handling `fnNameHere.foo(…)`. + node = node.property + } + } + + if (node.type === 'Identifier') { + if (!functions.has(node.name)) return + } + + meta.sortTextNodes = true + }, + StringLiteral(node, path, meta) { if (!meta.sortTextNodes) { return diff --git a/tests/plugins.test.ts b/tests/plugins.test.ts index 2a62368..aec305b 100644 --- a/tests/plugins.test.ts +++ b/tests/plugins.test.ts @@ -136,6 +136,7 @@ let tests: PluginTest[] = [ plugins: ['@zackad/prettier-plugin-twig'], options: { twigAlwaysBreakObjects: false, + tailwindFunctions: ['addClass', 'tw'], }, tests: { twig: [ @@ -164,6 +165,22 @@ let tests: PluginTest[] = [ `
`, ``, ], + + // Function call tests + t``, + t``, + + t`{{ tw('${yes}') }}`, + t`{{ attributes.addClass('${yes}') }}`, + + t`{{ tw('${yes}').tw('${yes}').tw('${yes}') }}`, + t`{{ attributes.addClass('${yes}').addClass('${yes}').addClass('${yes}') }}`, + + t`{% set className = '${no}' %} {{ attributes.addClass(className) }}`, + [ + `{{ attributes.addClass("sm:p-0 " ~ variant ~ " p-0") }}`, + `{{ attributes.addClass('sm:p-0 ' ~ variant ~ ' p-0') }}`, + ], ], }, },