Skip to content

Commit d33b650

Browse files
authored
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
1 parent c10ba4e commit d33b650

File tree

3 files changed

+86
-15
lines changed

3 files changed

+86
-15
lines changed

CHANGELOG.md

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

1920
## [3.2.1] - 2022-10-21
2021

src/lib/expandApplyAtRules.js

+35-15
Original file line numberDiff line numberDiff line change
@@ -346,22 +346,42 @@ function processApply(root, context, localCache) {
346346
})
347347
})
348348

349-
// Sort tag names before class names
349+
// Sort tag names before class names (but only sort each group (separated by a combinator)
350+
// separately and not in total)
350351
// This happens when replacing `.bar` in `.foo.bar` with a tag like `section`
351-
for (const sel of replaced) {
352-
sel.sort((a, b) => {
353-
if (a.type === 'tag' && b.type === 'class') {
354-
return -1
355-
} else if (a.type === 'class' && b.type === 'tag') {
356-
return 1
357-
} else if (a.type === 'class' && b.type === 'pseudo') {
358-
return -1
359-
} else if (a.type === 'pseudo' && b.type === 'class') {
360-
return 1
352+
for (let sel of replaced) {
353+
let groups = [[]]
354+
for (let node of sel.nodes) {
355+
if (node.type === 'combinator') {
356+
groups.push(node)
357+
groups.push([])
358+
} else {
359+
let last = groups[groups.length - 1]
360+
last.push(node)
361361
}
362+
}
362363

363-
return sel.index(a) - sel.index(b)
364-
})
364+
sel.nodes = []
365+
366+
for (let group of groups) {
367+
if (Array.isArray(group)) {
368+
group.sort((a, b) => {
369+
if (a.type === 'tag' && b.type === 'class') {
370+
return -1
371+
} else if (a.type === 'class' && b.type === 'tag') {
372+
return 1
373+
} else if (a.type === 'class' && b.type === 'pseudo') {
374+
return -1
375+
} else if (a.type === 'pseudo' && b.type === 'class') {
376+
return 1
377+
}
378+
379+
return 0
380+
})
381+
}
382+
383+
sel.nodes = sel.nodes.concat(group)
384+
}
365385
}
366386

367387
sel.replaceWith(...replaced)
@@ -382,7 +402,7 @@ function processApply(root, context, localCache) {
382402

383403
if (apply.parent.type === 'atrule') {
384404
if (apply.parent.name === 'screen') {
385-
const screenType = apply.parent.params
405+
let screenType = apply.parent.params
386406

387407
throw apply.error(
388408
`@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) {
414434
}
415435
}
416436

417-
for (const [parent, [candidates, atApplySource]] of perParentApplies) {
437+
for (let [parent, [candidates, atApplySource]] of perParentApplies) {
418438
let siblings = []
419439

420440
for (let [applyCandidate, important, rules] of candidates) {

tests/apply.test.js

+50
Original file line numberDiff line numberDiff line change
@@ -1691,3 +1691,53 @@ it('should not replace multiple instances of the same class in a single selector
16911691
}
16921692
`)
16931693
})
1694+
1695+
it('should maintain the correct selector when applying other utilities', () => {
1696+
let config = {
1697+
content: [
1698+
{
1699+
raw: html`
1700+
<div>
1701+
<div class="check"></div>
1702+
</div>
1703+
`,
1704+
},
1705+
],
1706+
}
1707+
1708+
let input = css`
1709+
@tailwind utilities;
1710+
1711+
.foo:hover.bar .baz {
1712+
@apply bg-black;
1713+
color: red;
1714+
}
1715+
1716+
.foo:hover.bar > .baz {
1717+
@apply bg-black;
1718+
color: red;
1719+
}
1720+
`
1721+
1722+
return run(input, config).then((result) => {
1723+
expect(result.css).toMatchFormattedCss(css`
1724+
.foo.bar:hover .baz {
1725+
--tw-bg-opacity: 1;
1726+
background-color: rgb(0 0 0 / var(--tw-bg-opacity));
1727+
}
1728+
1729+
.foo:hover.bar .baz {
1730+
color: red;
1731+
}
1732+
1733+
.foo.bar:hover > .baz {
1734+
--tw-bg-opacity: 1;
1735+
background-color: rgb(0 0 0 / var(--tw-bg-opacity));
1736+
}
1737+
1738+
.foo:hover.bar > .baz {
1739+
color: red;
1740+
}
1741+
`)
1742+
})
1743+
})

0 commit comments

Comments
 (0)