mirror of
https://github.com/tailwindlabs/tailwindcss.git
synced 2025-12-08 21:36:08 +00:00
Skip over classes inside :not(…) when nested in an at-rule (#12105)
* Skip over classes inside `:not(…)` when nested in an at-rule When defining a utility we skip over classes inside `:not(…)` but we missed doing this when classes were contained within an at-rule. This fixes that. * Update changelog
This commit is contained in:
parent
666c7e4566
commit
3fa8ab1793
@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Make `content` optional for presets in TypeScript types ([#11730](https://github.com/tailwindlabs/tailwindcss/pull/11730))
|
||||
- Handle variable colors that have variable fallback values ([#12049](https://github.com/tailwindlabs/tailwindcss/pull/12049))
|
||||
- Batch reading content files to prevent `too many open files` error ([#12079](https://github.com/tailwindlabs/tailwindcss/pull/12079))
|
||||
- Skip over classes inside `:not(…)` when nested in an at-rule ([#12105](https://github.com/tailwindlabs/tailwindcss/pull/12105))
|
||||
|
||||
## [3.3.3] - 2023-07-13
|
||||
|
||||
|
||||
@ -148,43 +148,45 @@ function getClasses(selector, mutate) {
|
||||
return parser.transformSync(selector)
|
||||
}
|
||||
|
||||
/**
|
||||
* Ignore everything inside a :not(...). This allows you to write code like
|
||||
* `div:not(.foo)`. If `.foo` is never found in your code, then we used to
|
||||
* not generated it. But now we will ignore everything inside a `:not`, so
|
||||
* that it still gets generated.
|
||||
*
|
||||
* @param {selectorParser.Root} selectors
|
||||
*/
|
||||
function ignoreNot(selectors) {
|
||||
selectors.walkPseudos((pseudo) => {
|
||||
if (pseudo.value === ':not') {
|
||||
pseudo.remove()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function extractCandidates(node, state = { containsNonOnDemandable: false }, depth = 0) {
|
||||
let classes = []
|
||||
let selectors = []
|
||||
|
||||
// Handle normal rules
|
||||
if (node.type === 'rule') {
|
||||
// Ignore everything inside a :not(...). This allows you to write code like
|
||||
// `div:not(.foo)`. If `.foo` is never found in your code, then we used to
|
||||
// not generated it. But now we will ignore everything inside a `:not`, so
|
||||
// that it still gets generated.
|
||||
function ignoreNot(selectors) {
|
||||
selectors.walkPseudos((pseudo) => {
|
||||
if (pseudo.value === ':not') {
|
||||
pseudo.remove()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
for (let selector of node.selectors) {
|
||||
let classCandidates = getClasses(selector, ignoreNot)
|
||||
// At least one of the selectors contains non-"on-demandable" candidates.
|
||||
if (classCandidates.length === 0) {
|
||||
state.containsNonOnDemandable = true
|
||||
}
|
||||
|
||||
for (let classCandidate of classCandidates) {
|
||||
classes.push(classCandidate)
|
||||
}
|
||||
}
|
||||
// Handle normal rules
|
||||
selectors.push(...node.selectors)
|
||||
} else if (node.type === 'atrule') {
|
||||
// Handle at-rules (which contains nested rules)
|
||||
node.walkRules((rule) => selectors.push(...rule.selectors))
|
||||
}
|
||||
|
||||
// Handle at-rules (which contains nested rules)
|
||||
else if (node.type === 'atrule') {
|
||||
node.walkRules((rule) => {
|
||||
for (let classCandidate of rule.selectors.flatMap((selector) => getClasses(selector))) {
|
||||
classes.push(classCandidate)
|
||||
}
|
||||
})
|
||||
for (let selector of selectors) {
|
||||
let classCandidates = getClasses(selector, ignoreNot)
|
||||
|
||||
// At least one of the selectors contains non-"on-demandable" candidates.
|
||||
if (classCandidates.length === 0) {
|
||||
state.containsNonOnDemandable = true
|
||||
}
|
||||
|
||||
for (let classCandidate of classCandidates) {
|
||||
classes.push(classCandidate)
|
||||
}
|
||||
}
|
||||
|
||||
if (depth === 0) {
|
||||
|
||||
@ -2,7 +2,7 @@ import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { crosscheck, run, html, css, defaults } from './util/run'
|
||||
|
||||
crosscheck(({ stable, oxide }) => {
|
||||
crosscheck(({ stable, oxide, engine }) => {
|
||||
test('basic usage', () => {
|
||||
let config = {
|
||||
content: [
|
||||
@ -1017,4 +1017,98 @@ crosscheck(({ stable, oxide }) => {
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
test('detects quoted arbitrary values containing a slash', async () => {
|
||||
let config = {
|
||||
content: [
|
||||
{
|
||||
raw: html`<div class="group-[[href^='/']]:hidden"></div>`,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
let input = css`
|
||||
@tailwind utilities;
|
||||
`
|
||||
|
||||
let result = await run(input, config)
|
||||
|
||||
expect(result.css).toMatchFormattedCss(
|
||||
engine.oxide
|
||||
? css`
|
||||
.group[href^='/'] .group-\[\[href\^\=\'\/\'\]\]\:hidden {
|
||||
display: none;
|
||||
}
|
||||
`
|
||||
: css`
|
||||
.hidden,
|
||||
.group[href^='/'] .group-\[\[href\^\=\'\/\'\]\]\:hidden {
|
||||
display: none;
|
||||
}
|
||||
`
|
||||
)
|
||||
})
|
||||
|
||||
test('handled quoted arbitrary values containing escaped spaces', async () => {
|
||||
let config = {
|
||||
content: [
|
||||
{
|
||||
raw: html`<div class="group-[[href^='_bar']]:hidden"></div>`,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
let input = css`
|
||||
@tailwind utilities;
|
||||
`
|
||||
|
||||
let result = await run(input, config)
|
||||
|
||||
expect(result.css).toMatchFormattedCss(
|
||||
engine.oxide
|
||||
? css`
|
||||
.group[href^=' bar'] .group-\[\[href\^\=\'_bar\'\]\]\:hidden {
|
||||
display: none;
|
||||
}
|
||||
`
|
||||
: css`
|
||||
.hidden,
|
||||
.group[href^=' bar'] .group-\[\[href\^\=\'_bar\'\]\]\:hidden {
|
||||
display: none;
|
||||
}
|
||||
`
|
||||
)
|
||||
})
|
||||
|
||||
test('Skips classes inside :not() when nested inside an at-rule', async () => {
|
||||
let config = {
|
||||
content: [
|
||||
{
|
||||
raw: html` <div class="disabled !disabled"></div> `,
|
||||
},
|
||||
],
|
||||
corePlugins: { preflight: false },
|
||||
plugins: [
|
||||
function ({ addUtilities }) {
|
||||
addUtilities({
|
||||
'.hand:not(.disabled)': {
|
||||
'@supports (cursor: pointer)': {
|
||||
cursor: 'pointer',
|
||||
},
|
||||
},
|
||||
})
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
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``)
|
||||
})
|
||||
})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user