diff --git a/CHANGELOG.md b/CHANGELOG.md index eb0672a2bafe..49ce94a4ac41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/src/lib/expandApplyAtRules.js b/src/lib/expandApplyAtRules.js index 7dad021f0d82..e1453af0152a 100644 --- a/src/lib/expandApplyAtRules.js +++ b/src/lib/expandApplyAtRules.js @@ -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) } + } - return sel.index(a) - sel.index(b) - }) + 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 + }) + } + + 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) { diff --git a/tests/apply.test.js b/tests/apply.test.js index b1ef198145c6..5eb8ecb38151 100644 --- a/tests/apply.test.js +++ b/tests/apply.test.js @@ -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` +