From 939fda6f4e101ff3d378e0d51c20a8baa229afff Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Fri, 18 Jul 2025 12:31:19 -0400 Subject: [PATCH] Show more specific error for functional-like but invalid utility names (#18568) Fixes #18566 The behavior is by design but the error message could definitely be improved. --- CHANGELOG.md | 1 + packages/tailwindcss/src/index.test.ts | 24 ++++++++++++++++++++++++ packages/tailwindcss/src/index.ts | 12 ++++++++++++ 3 files changed, 37 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f088d873a..0b09cfdb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Ignore consecutive semicolons in the CSS parser ([#18532](https://github.com/tailwindlabs/tailwindcss/pull/18532)) - Center the dropdown icon added to an input with a paired datalist ([#18511](https://github.com/tailwindlabs/tailwindcss/pull/18511)) - Extract candidates in Slang templates ([#18565](https://github.com/tailwindlabs/tailwindcss/pull/18565)) +- Improve error messages when encountering invalid functional utility names ([#18568](https://github.com/tailwindlabs/tailwindcss/pull/18568)) ## [4.1.11] - 2025-06-26 diff --git a/packages/tailwindcss/src/index.test.ts b/packages/tailwindcss/src/index.test.ts index 430c3a71d..a19fcf96d 100644 --- a/packages/tailwindcss/src/index.test.ts +++ b/packages/tailwindcss/src/index.test.ts @@ -4354,6 +4354,30 @@ describe('@utility', () => { `[Error: \`@utility 💨\` defines an invalid utility name. Utilities should be alphanumeric and start with a lowercase letter.]`, ) }) + + test('A functional @utility must end in -*', () => { + return expect( + compileCss(css` + @utility foo* { + color: red; + } + `), + ).rejects.toThrowErrorMatchingInlineSnapshot( + `[Error: \`@utility foo*\` defines an invalid utility name. A functional utility must end in \`-*\`.]`, + ) + }) + + test('Only the last part of a functional @utility can be dynamic', () => { + return expect( + compileCss(css` + @utility my-*-utility { + color: red; + } + `), + ).rejects.toThrowErrorMatchingInlineSnapshot( + `[Error: \`@utility my-*-utility\` defines an invalid utility name. The dynamic portion marked by \`-*\` must appear once at the end.]`, + ) + }) }) test('addBase', async () => { diff --git a/packages/tailwindcss/src/index.ts b/packages/tailwindcss/src/index.ts index 4907306ee..07a23a325 100644 --- a/packages/tailwindcss/src/index.ts +++ b/packages/tailwindcss/src/index.ts @@ -229,6 +229,18 @@ async function parseCss( let utility = createCssUtility(node) if (utility === null) { + if (!node.params.endsWith('-*')) { + if (node.params.endsWith('*')) { + throw new Error( + `\`@utility ${node.params}\` defines an invalid utility name. A functional utility must end in \`-*\`.`, + ) + } else if (node.params.includes('*')) { + throw new Error( + `\`@utility ${node.params}\` defines an invalid utility name. The dynamic portion marked by \`-*\` must appear once at the end.`, + ) + } + } + throw new Error( `\`@utility ${node.params}\` defines an invalid utility name. Utilities should be alphanumeric and start with a lowercase letter.`, )