diff --git a/CHANGELOG.md b/CHANGELOG.md index ca9046c08..e13baa42f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Use absolute paths when resolving changed files for resilience against working directory changes ([#9032](https://github.com/tailwindlabs/tailwindcss/pull/9032)) - Fix ring color utility generation when using `respectDefaultRingColorOpacity` ([#9070](https://github.com/tailwindlabs/tailwindcss/pull/9070)) -- Replace all occurrences of a class in a selector when using `@apply` ([#9107](https://github.com/tailwindlabs/tailwindcss/pull/9107)) - Sort tags before classes when `@applying` a selector with joined classes ([#9107](https://github.com/tailwindlabs/tailwindcss/pull/9107)) ## [3.1.8] - 2022-08-05 diff --git a/src/lib/expandApplyAtRules.js b/src/lib/expandApplyAtRules.js index 6d23b8811..7c4d156e0 100644 --- a/src/lib/expandApplyAtRules.js +++ b/src/lib/expandApplyAtRules.js @@ -315,6 +315,7 @@ function processApply(root, context, localCache) { let replaced = new Set() utilitySelectorsList.each((utilitySelector) => { + let hasReplaced = false utilitySelector = utilitySelector.clone() utilitySelector.walkClasses((node) => { @@ -322,6 +323,19 @@ function processApply(root, context, localCache) { return } + // Don't replace multiple instances of the same class + // This is theoretically correct but only partially + // We'd need to generate every possible permutation of the replacement + // For example with `.foo + .foo { … }` and `section { @apply foo; }` + // We'd need to generate all of these: + // - `.foo + .foo` + // - `.foo + section` + // - `section + .foo` + // - `section + section` + if (hasReplaced) { + return + } + // Since you can only `@apply` class names this is sufficient // We want to replace the matched class name with the selector the user is using // Ex: Replace `.text-blue-500` with `.foo.bar:is(.something-cool)` @@ -329,6 +343,8 @@ function processApply(root, context, localCache) { // Record that we did something and we want to use this new selector replaced.add(utilitySelector) + + hasReplaced = true }) }) diff --git a/tests/apply.test.js b/tests/apply.test.js index 300aa8a8c..b1ef19814 100644 --- a/tests/apply.test.js +++ b/tests/apply.test.js @@ -1641,7 +1641,10 @@ it('can apply joined classes when using elements', async () => { `) }) -it('can produce selectors that replace multiple instances of the same class', async () => { +it('should not replace multiple instances of the same class in a single selector', async () => { + // NOTE: This test is non-normative and is not part of the spec of how `@apply` works per-se + // It describes how it currently works because the "correct" way produces a combinatorial explosion + // of selectors that is not easily doable let config = { content: [{ raw: html`
` }], plugins: [], @@ -1674,14 +1677,16 @@ it('can produce selectors that replace multiple instances of the same class', as .bar + .bar { color: fuchsia; } - header + header { + header + .foo { color: blue; } - main + main { + main + .foo { color: blue; + } + main + .bar { color: fuchsia; } - footer + footer { + footer + .bar { color: fuchsia; } `)