Skip to content

Commit

Permalink
Ensure @apply of a rule inside an AtRule works (#6594)
Browse files Browse the repository at this point in the history
* ensure apply of rule inside atrule works

If that atrule happens to contain another rule that is technically
unrelated.

Co-authored-by: Jordan Pittman <jordan@cryptica.me>

* update changelog

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
  • Loading branch information
RobinMalfait and thecrypticace committed Dec 17, 2021
1 parent 9e03a68 commit 86e73b2
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Only generate variants for non-`user` layers ([#6589](https://github.com/tailwindlabs/tailwindcss/pull/6589))
- Properly extract classes with arbitrary values in arrays and classes followed by escaped quotes ([#6590](https://github.com/tailwindlabs/tailwindcss/pull/6590))
- Improve jsx interpolation candidate matching ([#6593](https://github.com/tailwindlabs/tailwindcss/pull/6593))
- Ensure `@apply` of a rule inside an AtRule works ([#6594](https://github.com/tailwindlabs/tailwindcss/pull/6594))

## [3.0.6] - 2021-12-16

Expand Down
36 changes: 36 additions & 0 deletions src/lib/expandApplyAtRules.js
Expand Up @@ -266,6 +266,42 @@ function processApply(root, context) {

if (canRewriteSelector) {
root.walkRules((rule) => {
// Let's imagine you have the following structure:
//
// .foo {
// @apply bar;
// }
//
// @supports (a: b) {
// .bar {
// color: blue
// }
//
// .something-unrelated {}
// }
//
// In this case we want to apply `.bar` but it happens to be in
// an atrule node. We clone that node instead of the nested one
// because we still want that @supports rule to be there once we
// applied everything.
//
// However it happens to be that the `.something-unrelated` is
// also in that same shared @supports atrule. This is not good,
// and this should not be there. The good part is that this is
// a clone already and it can be safely removed. The question is
// how do we know we can remove it. Basically what we can do is
// match it against the applyCandidate that you want to apply. If
// it doesn't match the we can safely delete it.
//
// If we didn't do this, then the `replaceSelector` function
// would have replaced this with something that didn't exist and
// therefore it removed the selector altogether. In this specific
// case it would result in `{}` instead of `.something-unrelated {}`
if (!extractClasses(rule).some((thing) => thing === applyCandidate)) {
rule.remove()
return
}

rule.selector = replaceSelector(parent.selector, rule.selector, applyCandidate)

rule.walkDecls((d) => {
Expand Down
62 changes: 62 additions & 0 deletions tests/apply.test.js
Expand Up @@ -712,3 +712,65 @@ it('should not be possible to apply user css with variants', () => {
)
})
})

it('should not apply unrelated siblings when applying something from within atrules', () => {
let config = {
content: [{ raw: html`<div class="foo bar something-unrelated"></div>` }],
plugins: [],
}

let input = css`
@tailwind components;
@tailwind utilities;
@layer components {
.foo {
font-weight: bold;
@apply bar;
}
.bar {
color: green;
}
@supports (a: b) {
.bar {
color: blue;
}
.something-unrelated {
color: red;
}
}
}
`

return run(input, config).then((result) => {
expect(result.css).toMatchFormattedCss(css`
.foo {
font-weight: bold;
color: green;
}
@supports (a: b) {
.foo {
color: blue;
}
}
.bar {
color: green;
}
@supports (a: b) {
.bar {
color: blue;
}
.something-unrelated {
color: red;
}
}
`)
})
})

0 comments on commit 86e73b2

Please sign in to comment.