diff --git a/CHANGELOG.md b/CHANGELOG.md index 415a61da09d5..6ae90ce6d6ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Don’t reorder webkit scrollbar pseudo elements ([#9991](https://github.com/tailwindlabs/tailwindcss/pull/9991)) - Deterministic sorting of arbitrary variants ([#10016](https://github.com/tailwindlabs/tailwindcss/pull/10016)) - Add `data` key to theme types ([#10023](https://github.com/tailwindlabs/tailwindcss/pull/10023)) +- Prevent invalid arbitrary variant selectors from failing the build ([#10059](https://github.com/tailwindlabs/tailwindcss/pull/10059)) ### Changed diff --git a/src/lib/generateRules.js b/src/lib/generateRules.js index e4b286b69220..636b9eaac0d4 100644 --- a/src/lib/generateRules.js +++ b/src/lib/generateRules.js @@ -733,6 +733,8 @@ function* resolveMatches(candidate, context, original = candidate) { } for (let match of matches) { + let isValid = true + match[1].raws.tailwind = { ...match[1].raws.tailwind, candidate } // Apply final format selector @@ -742,7 +744,7 @@ function* resolveMatches(candidate, context, original = candidate) { container.walkRules((rule) => { if (inKeyframes(rule)) return - rule.selector = finalizeSelector(finalFormat, { + let selectorOptions = { selector: rule.selector, candidate: original, base: candidate @@ -751,11 +753,31 @@ function* resolveMatches(candidate, context, original = candidate) { isArbitraryVariant: match[0].isArbitraryVariant, context, - }) + } + + try { + rule.selector = finalizeSelector(finalFormat, selectorOptions) + } catch { + // The selector we produced is invalid + // This could be because: + // - A bug exists + // - A plugin introduced an invalid variant selector (ex: `addVariant('foo', '&;foo')`) + // - The user used an invalid arbitrary variant (ex: `[&;foo]:underline`) + // Either way the build will fail because of this + // We would rather that the build pass "silently" given that this could + // happen because of picking up invalid things when scanning content + // So we'll throw out the candidate instead + isValid = false + return false + } }) match[1] = container.nodes[0] } + if (!isValid) { + continue + } + yield match } } diff --git a/tests/arbitrary-variants.test.js b/tests/arbitrary-variants.test.js index f51b20272b4d..80d854f03c93 100644 --- a/tests/arbitrary-variants.test.js +++ b/tests/arbitrary-variants.test.js @@ -1099,3 +1099,47 @@ it('Arbitrary variants are ordered alphabetically', () => { `) }) }) + +it('Arbitrary variants support multiple attribute selectors', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + [data-foo='bar'][data-baz] .\[\[data-foo\=\'bar\'\]\[data-baz\]_\&\]\:underline { + text-decoration-line: underline; + } + `) + }) +}) + +it('Invalid arbitrary variants selectors should produce nothing instead of failing', () => { + let config = { + content: [ + { + raw: html` +
+ `, + }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css``) + }) +})