Fix incorrect selectors when using @apply in selectors with combinators and pseudos (#9722)

* sort tags, classes and pseudos per group (separated by combinators)

* use default behaviour of sort

* update changelog
This commit is contained in:
Robin Malfait 2022-11-03 12:20:38 +01:00 committed by GitHub
parent c10ba4e9ba
commit d33b6503ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 86 additions and 15 deletions

View File

@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fix merging of arrays during config resolution ([#9706](https://github.com/tailwindlabs/tailwindcss/issues/9706))
- Ensure configured `font-feature-settings` are included in Preflight ([#9707](https://github.com/tailwindlabs/tailwindcss/pull/9707))
- Fix fractional values not being parsed properly inside arbitrary properties ([#9705](https://github.com/tailwindlabs/tailwindcss/pull/9705))
- Fix incorrect selectors when using `@apply` in selectors with combinators and pseudos ([#9722](https://github.com/tailwindlabs/tailwindcss/pull/9722))
## [3.2.1] - 2022-10-21

View File

@ -346,22 +346,42 @@ function processApply(root, context, localCache) {
})
})
// Sort tag names before class names
// Sort tag names before class names (but only sort each group (separated by a combinator)
// separately and not in total)
// This happens when replacing `.bar` in `.foo.bar` with a tag like `section`
for (const sel of replaced) {
sel.sort((a, b) => {
if (a.type === 'tag' && b.type === 'class') {
return -1
} else if (a.type === 'class' && b.type === 'tag') {
return 1
} else if (a.type === 'class' && b.type === 'pseudo') {
return -1
} else if (a.type === 'pseudo' && b.type === 'class') {
return 1
for (let sel of replaced) {
let groups = [[]]
for (let node of sel.nodes) {
if (node.type === 'combinator') {
groups.push(node)
groups.push([])
} else {
let last = groups[groups.length - 1]
last.push(node)
}
}
sel.nodes = []
for (let group of groups) {
if (Array.isArray(group)) {
group.sort((a, b) => {
if (a.type === 'tag' && b.type === 'class') {
return -1
} else if (a.type === 'class' && b.type === 'tag') {
return 1
} else if (a.type === 'class' && b.type === 'pseudo') {
return -1
} else if (a.type === 'pseudo' && b.type === 'class') {
return 1
}
return 0
})
}
return sel.index(a) - sel.index(b)
})
sel.nodes = sel.nodes.concat(group)
}
}
sel.replaceWith(...replaced)
@ -382,7 +402,7 @@ function processApply(root, context, localCache) {
if (apply.parent.type === 'atrule') {
if (apply.parent.name === 'screen') {
const screenType = apply.parent.params
let screenType = apply.parent.params
throw apply.error(
`@apply is not supported within nested at-rules like @screen. We suggest you write this as @apply ${applyCandidates
@ -414,7 +434,7 @@ function processApply(root, context, localCache) {
}
}
for (const [parent, [candidates, atApplySource]] of perParentApplies) {
for (let [parent, [candidates, atApplySource]] of perParentApplies) {
let siblings = []
for (let [applyCandidate, important, rules] of candidates) {

View File

@ -1691,3 +1691,53 @@ it('should not replace multiple instances of the same class in a single selector
}
`)
})
it('should maintain the correct selector when applying other utilities', () => {
let config = {
content: [
{
raw: html`
<div>
<div class="check"></div>
</div>
`,
},
],
}
let input = css`
@tailwind utilities;
.foo:hover.bar .baz {
@apply bg-black;
color: red;
}
.foo:hover.bar > .baz {
@apply bg-black;
color: red;
}
`
return run(input, config).then((result) => {
expect(result.css).toMatchFormattedCss(css`
.foo.bar:hover .baz {
--tw-bg-opacity: 1;
background-color: rgb(0 0 0 / var(--tw-bg-opacity));
}
.foo:hover.bar .baz {
color: red;
}
.foo.bar:hover > .baz {
--tw-bg-opacity: 1;
background-color: rgb(0 0 0 / var(--tw-bg-opacity));
}
.foo:hover.bar > .baz {
color: red;
}
`)
})
})