Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Eliminate irrelevant rules when applying variants #12113

Merged
merged 2 commits into from
Sep 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Skip over classes inside `:not(…)` when nested in an at-rule ([#12105](https://github.com/tailwindlabs/tailwindcss/pull/12105))
- Update types to work with `Node16` module resolution ([#12097](https://github.com/tailwindlabs/tailwindcss/pull/12097))
- Don’t crash when important and parent selectors are equal in `@apply` ([#12112](https://github.com/tailwindlabs/tailwindcss/pull/12112))
- Eliminate irrelevant rules when applying variants ([#12113](https://github.com/tailwindlabs/tailwindcss/pull/12113))

### Added

Expand Down
11 changes: 10 additions & 1 deletion src/lib/generateRules.js
Original file line number Diff line number Diff line change
Expand Up @@ -815,10 +815,19 @@ function applyFinalFormat(match, { context, candidate }) {
}

try {
rule.selector = finalizeSelector(rule.selector, finalFormat, {
let selector = finalizeSelector(rule.selector, finalFormat, {
candidate,
context,
})

// Finalize Selector determined that this candidate is irrelevant
// TODO: This elimination should happen earlier so this never happens
if (selector === null) {
rule.remove()
return
}

rule.selector = selector
} catch {
// If this selector is invalid we also want to skip it
// But it's likely that being invalid here means there's a bug in a plugin rather than too loosely matching content
Expand Down
7 changes: 7 additions & 0 deletions src/util/formatVariantSelector.js
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,13 @@ export function finalizeSelector(current, formats, { context, candidate, base })
// Remove extraneous selectors that do not include the base candidate
selector.each((sel) => eliminateIrrelevantSelectors(sel, base))

// If ffter eliminating irrelevant selectors, we end up with nothing
// Then the whole "rule" this is associated with does not need to exist
// We use `null` as a marker value for that case
if (selector.length === 0) {
return null
}

// If there are no formats that means there were no variants added to the candidate
// so we can just return the selector as-is
let formatAst = Array.isArray(formats)
Expand Down
43 changes: 43 additions & 0 deletions tests/basic-usage.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -792,3 +792,46 @@ test('Skips classes inside :not() when nested inside an at-rule', async () => {

expect(result.css).toMatchFormattedCss(css``)
})

test('Irrelevant rules are removed when applying variants', async () => {
let config = {
content: [
{
raw: html` <div class="md:w-full"></div> `,
},
],
corePlugins: { preflight: false },
plugins: [
function ({ addUtilities }) {
addUtilities({
'@supports (foo: bar)': {
// This doesn't contain `w-full` so it should not exist in the output
'.outer': { color: 'red' },
'.outer:is(.w-full)': { color: 'green' },
},
})
},
],
}

let input = css`
@tailwind utilities;
`

// We didn't find the hand class therefore
// nothing should be generated
let result = await run(input, config)

expect(result.css).toMatchFormattedCss(css`
@media (min-width: 768px) {
.md\:w-full {
width: 100%;
}
@supports (foo: bar) {
.outer.md\:w-full {
color: green;
}
}
}
`)
})
Loading