Skip to content

Commit 2792c22

Browse files
committed
Nested mixins' declarations should no longer be reordered when flattening nested rules
1 parent fdddb24 commit 2792c22

File tree

9 files changed

+82
-6
lines changed

9 files changed

+82
-6
lines changed

plugins/postcss-nesting/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changes to PostCSS Nesting
22

3+
### Unreleased (patch)
4+
5+
- Fix reorder of declarations inside nested `@mixin` rules
6+
37
### 12.0.3
48

59
_February 19, 2024_

plugins/postcss-nesting/dist/index.cjs

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

plugins/postcss-nesting/dist/index.mjs

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

plugins/postcss-nesting/src/lib/group-declarations.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
import type { ChildNode, Container } from 'postcss';
22

3+
function isMixinAtRule(node: ChildNode): boolean {
4+
return node.type === 'atrule' && node.name.toLowerCase() === 'mixin';
5+
}
6+
7+
function isNonMixinAtRule(node: ChildNode): boolean {
8+
return node.type === 'atrule' && !isMixinAtRule(node);
9+
}
10+
311
export default function groupDeclarations(node: Container<ChildNode>) {
412
// https://drafts.csswg.org/css-nesting/#mixing
513
// When a style rule contains both declarations and nested style rules or nested conditional group rules,
@@ -24,13 +32,13 @@ export default function groupDeclarations(node: Container<ChildNode>) {
2432
return;
2533
}
2634

27-
if (child.type === 'atrule' && child.name.toLowerCase() === 'mixin') {
35+
if (isMixinAtRule(child)) {
2836
let prev = child.prev();
2937
// We assume that
3038
// - a mixin after declarations will resolve to more declarations
31-
// - a mixin after rules or at-rules will resolve to more rules or at-rules
39+
// - a mixin after rules or at-rules will resolve to more rules or at-rules (except after another mixin)
3240
while (prev) {
33-
if ((prev.type === 'rule' || prev.type === 'atrule')) {
41+
if ((prev.type === 'rule' || (isNonMixinAtRule(prev)))) {
3442
return;
3543
}
3644

@@ -50,7 +58,7 @@ export default function groupDeclarations(node: Container<ChildNode>) {
5058

5159
if (child.type === 'comment') {
5260
const next = child.next();
53-
if (next && (next.type === 'comment' || next.type === 'rule' || (next.type === 'atrule' && next.name.toLowerCase() !== 'mixin'))) {
61+
if (next && (next.type === 'comment' || next.type === 'rule' || isNonMixinAtRule(next))) {
5462
return;
5563
}
5664

plugins/postcss-nesting/test/_tape.mjs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,25 @@ const mixinPluginDeclaration = () => {
2727

2828
mixinPluginDeclaration.postcss = true;
2929

30+
const mixinPluginNestedRules = () => {
31+
return {
32+
postcssPlugin: 'mixin',
33+
AtRule: {
34+
mixin(node, { postcss }) {
35+
if (node.params === 'nestedMixins') {
36+
node.replaceWith(postcss.parse('@mixin mixinWithDecl; @mixin mixinToOverride; &:disabled { color: white; }', {from : 'mixin.css'}));
37+
} else if (node.params === 'mixinWithDecl') {
38+
node.replaceWith(postcss.parse('color: blue;', {from : 'mixin.css'}));
39+
} else if (node.params === 'mixinToOverride') {
40+
node.replaceWith(postcss.parse('display: flex;', {from : 'mixin.css'}));
41+
}
42+
},
43+
},
44+
};
45+
};
46+
47+
mixinPluginNestedRules.postcss = true;
48+
3049
postcssTape(plugin)({
3150
'basic': {
3251
message: 'supports basic usage',
@@ -168,6 +187,14 @@ postcssTape(plugin)({
168187
noIsPseudoSelector: true,
169188
},
170189
},
190+
'mixin-nested-rules': {
191+
message: 'supports mixin with nested rules',
192+
plugins: [mixinPluginNestedRules(), plugin()],
193+
},
194+
'mixin-nested-rules-after-media': {
195+
message: 'supports mixin with nested rules',
196+
plugins: [mixinPluginNestedRules(), plugin()],
197+
},
171198
'spec-examples': {
172199
message: 'supports all spec examples',
173200
},
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
a {
2+
color: yellow;
3+
display: inline-flex;
4+
5+
@media (min-width: 768px) {
6+
color: red;
7+
}
8+
9+
@mixin nestedMixins;
10+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
a {
2+
color: yellow;
3+
display: inline-flex;
4+
}
5+
@media (min-width: 768px) {
6+
a {
7+
color: red;
8+
}
9+
}
10+
a {color: blue;display: flex;
11+
}
12+
a:disabled { color: white; }
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
a {
2+
color: yellow;
3+
4+
& b {
5+
@mixin nestedMixins;
6+
display: inline-flex;
7+
}
8+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
a {
2+
color: yellow;
3+
}
4+
a b {color: blue;display: flex;
5+
display: inline-flex;
6+
}
7+
a b:disabled { color: white; }

0 commit comments

Comments
 (0)