From 924dd69bdb5a284f9f64efbbbb7ec799b0ab912c Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Wed, 29 Jan 2025 16:09:09 +0100 Subject: [PATCH] Ensure `@custom-variant foo ()` has a non-empty selector list (#16009) This PR fixes an issue where an empty selector list was valid when defining a `@custom-variant`. Given this input: ```css @custom-variant foo (); ``` If you then use it with a utility such as `foo:flex`, then the following (incorrect) CSS was generated: ```css .foo\:flex { { display: flex; } } ``` Which is invalid CSS. This PR will now validate that that we have at least _something_ and show an error accordingly. --- CHANGELOG.md | 1 + packages/tailwindcss/src/index.test.ts | 20 ++++++++++++++++++++ packages/tailwindcss/src/index.ts | 5 +++++ 3 files changed, 26 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 14e0f19fb6a7..ad67eb77b4f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Find utilities when using functions inside arrays ([#15974](https://github.com/tailwindlabs/tailwindcss/pull/15974)) - Ensure that `@tailwindcss/browser` does not pollute the global namespace ([#15978](https://github.com/tailwindlabs/tailwindcss/pull/15978)) - Fix crash when project lives in the `/` directory ([#15988](https://github.com/tailwindlabs/tailwindcss/pull/15988)) +- Ensure `@custom-variant` has a non-empty selector list ([#16009](https://github.com/tailwindlabs/tailwindcss/pull/16009)) - _Upgrade_: Ensure JavaScript config files on different drives are correctly migrated ([#15927](https://github.com/tailwindlabs/tailwindcss/pull/15927)) - _Upgrade_: Migrate `leading-[1]` to `leading-none` ([#16004](https://github.com/tailwindlabs/tailwindcss/pull/16004)) - _Upgrade_: Do not migrate arbitrary leading utilities to bare utilities ([#16004](https://github.com/tailwindlabs/tailwindcss/pull/16004)) diff --git a/packages/tailwindcss/src/index.test.ts b/packages/tailwindcss/src/index.test.ts index 0b5851b70c8b..fdf06077bc3c 100644 --- a/packages/tailwindcss/src/index.test.ts +++ b/packages/tailwindcss/src/index.test.ts @@ -2514,6 +2514,26 @@ describe('@custom-variant', () => { ).rejects.toThrowErrorMatchingInlineSnapshot(`[Error: \`@custom-variant\` cannot be nested.]`) }) + test('@custom-variant must not have an empty selector', () => { + return expect( + compileCss(css` + @custom-variant foo (); + `), + ).rejects.toThrowErrorMatchingInlineSnapshot( + `[Error: \`@custom-variant foo ()\` selector is invalid.]`, + ) + }) + + test('@custom-variant with multiple selectors, cannot be empty', () => { + return expect( + compileCss(css` + @custom-variant foo (.foo, .bar, ); + `), + ).rejects.toThrowErrorMatchingInlineSnapshot( + `[Error: \`@custom-variant foo (.foo, .bar, )\` selector is invalid.]`, + ) + }) + test('@custom-variant with no body must include a selector', () => { return expect( compileCss(css` diff --git a/packages/tailwindcss/src/index.ts b/packages/tailwindcss/src/index.ts index 3e4bc5a3cce0..77951a3e8591 100644 --- a/packages/tailwindcss/src/index.ts +++ b/packages/tailwindcss/src/index.ts @@ -281,6 +281,11 @@ async function parseCss( } let selectors = segment(selector.slice(1, -1), ',') + if (selectors.length === 0 || selectors.some((selector) => selector.trim() === '')) { + throw new Error( + `\`@custom-variant ${name} (${selectors.join(',')})\` selector is invalid.`, + ) + } let atRuleParams: string[] = [] let styleRuleSelectors: string[] = []