Skip to content

Commit 48728ed

Browse files
authored
Fix generation of div:not(.foo) if .foo is never defined (#7815)
* fix little typo * ensure that `div:not(.unknown-class)` gets generated * update changelog
1 parent 7b4cc36 commit 48728ed

File tree

3 files changed

+48
-6
lines changed

3 files changed

+48
-6
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1919
- Correctly parse and prefix animation names with dots ([#7163](https://github.com/tailwindlabs/tailwindcss/pull/7163))
2020
- Fix extraction from template literal/function with array ([#7481](https://github.com/tailwindlabs/tailwindcss/pull/7481))
2121
- Don't output unparsable arbitrary values ([#7789](https://github.com/tailwindlabs/tailwindcss/pull/7789))
22+
- Fix generation of `div:not(.foo)` if `.foo` is never defined ([#7815](https://github.com/tailwindlabs/tailwindcss/pull/7815))
2223

2324
### Changed
2425

src/lib/setupContextUtils.js

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,18 @@ function parseStyles(styles) {
8585
})
8686
}
8787

88-
function getClasses(selector) {
88+
function getClasses(selector, mutate) {
8989
let parser = selectorParser((selectors) => {
9090
let allClasses = []
91+
92+
if (mutate) {
93+
mutate(selectors)
94+
}
95+
9196
selectors.walkClasses((classNode) => {
9297
allClasses.push(classNode.value)
9398
})
99+
94100
return allClasses
95101
})
96102
return parser.transformSync(selector)
@@ -101,8 +107,20 @@ function extractCandidates(node, state = { containsNonOnDemandable: false }, dep
101107

102108
// Handle normal rules
103109
if (node.type === 'rule') {
110+
// Ignore everything inside a :not(...). This allows you to write code like
111+
// `div:not(.foo)`. If `.foo` is never found in your code, then we used to
112+
// not generated it. But now we will ignore everything inside a `:not`, so
113+
// that it still gets generated.
114+
function ignoreNot(selectors) {
115+
selectors.walkPseudos((pseudo) => {
116+
if (pseudo.value === ':not') {
117+
pseudo.remove()
118+
}
119+
})
120+
}
121+
104122
for (let selector of node.selectors) {
105-
let classCandidates = getClasses(selector)
123+
let classCandidates = getClasses(selector, ignoreNot)
106124
// At least one of the selectors contains non-"on-demandable" candidates.
107125
if (classCandidates.length === 0) {
108126
state.containsNonOnDemandable = true
@@ -117,9 +135,7 @@ function extractCandidates(node, state = { containsNonOnDemandable: false }, dep
117135
// Handle at-rules (which contains nested rules)
118136
else if (node.type === 'atrule') {
119137
node.walkRules((rule) => {
120-
for (let classCandidate of rule.selectors.flatMap((selector) =>
121-
getClasses(selector, state, depth + 1)
122-
)) {
138+
for (let classCandidate of rule.selectors.flatMap((selector) => getClasses(selector))) {
123139
classes.push(classCandidate)
124140
}
125141
})

tests/basic-usage.test.js

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ it('does not produce duplicate output when seeing variants preceding a wildcard
349349
})
350350
})
351351

352-
it('it can parse box shadows with variables', () => {
352+
it('can parse box shadows with variables', () => {
353353
let config = {
354354
content: [{ raw: html`<div class="shadow-lg"></div>` }],
355355
theme: {
@@ -376,3 +376,28 @@ it('it can parse box shadows with variables', () => {
376376
`)
377377
})
378378
})
379+
380+
it('should generate styles using :not(.unknown-class) even if `.unknown-class` does not exist', () => {
381+
let config = {
382+
content: [{ raw: html`<div></div>` }],
383+
corePlugins: { preflight: false },
384+
}
385+
386+
let input = css`
387+
@tailwind components;
388+
389+
@layer components {
390+
div:not(.unknown-class) {
391+
color: red;
392+
}
393+
}
394+
`
395+
396+
return run(input, config).then((result) => {
397+
expect(result.css).toMatchFormattedCss(css`
398+
div:not(.unknown-class) {
399+
color: red;
400+
}
401+
`)
402+
})
403+
})

0 commit comments

Comments
 (0)