Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add mixin-no-risky-parent-selectors rule.
- Loading branch information
1 parent
26c105c
commit 5b83763
Showing
5 changed files
with
419 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
# at-mixin-no-risky-nesting-selector | ||
|
||
Disallow risky nesting selectors within a mixin. | ||
|
||
If a mixin contains a parent selector within another style rule, and is used in a nested context, | ||
the output selector may include the outermost parent selector in an unexpected way. | ||
|
||
In this example: | ||
```scss | ||
@mixin foo { | ||
.a { | ||
color: blue; | ||
.b & { | ||
color: red; | ||
} | ||
} | ||
} | ||
|
||
.c { | ||
@include foo; | ||
} | ||
``` | ||
|
||
The user may expect `.c` to go outside all selectors in `foo`: | ||
`.c .b .a {...}` | ||
|
||
But this outputs: | ||
```scss | ||
.c .a { | ||
color: blue; | ||
} | ||
.b .c .a { | ||
color: red; | ||
} | ||
``` | ||
|
||
However, if we pull the parent selector into the child and make the child style rule a sibling: | ||
```scss | ||
@mixin foo { | ||
.a { | ||
color: blue; | ||
} | ||
.b .a { | ||
color: red; | ||
} | ||
} | ||
|
||
.c { | ||
@include foo; | ||
} | ||
``` | ||
|
||
It outputs: | ||
```scss | ||
.c .a { | ||
color: blue; | ||
} | ||
.c .b .a { | ||
color: red; | ||
} | ||
``` | ||
|
||
This only occurs when a parent selector meets all of the following conditions: | ||
- Is within a `@mixin` rule. | ||
- Is nested within another style rule. | ||
- Is not positioned at the beginning of a complex selector. | ||
|
||
## Options | ||
|
||
### `true` | ||
|
||
The following patterns are considered warnings: | ||
|
||
```scss | ||
@mixin foo { | ||
.bar { | ||
color: blue; | ||
.baz & { | ||
color: red; | ||
} | ||
} | ||
} | ||
``` | ||
|
||
```scss | ||
@mixin foo { | ||
.bar { | ||
color: blue; | ||
.qux, .baz & .quux{ | ||
color: red; | ||
} | ||
} | ||
} | ||
``` | ||
|
||
The following patterns are _not_ considered warnings: | ||
|
||
```scss | ||
.foo { | ||
.bar { | ||
color: blue; | ||
.baz & { | ||
color: red; | ||
} | ||
} | ||
} | ||
``` | ||
|
||
```scss | ||
@mixin foo { | ||
.bar { | ||
color: blue; | ||
& .baz { | ||
color: red; | ||
} | ||
} | ||
} | ||
``` | ||
|
||
```scss | ||
.bar { | ||
color: blue; | ||
.baz & { | ||
color: red; | ||
} | ||
} | ||
``` | ||
|
||
```scss | ||
.foo { | ||
color: blue; | ||
& .bar, .baz & .qux { | ||
color: red; | ||
} | ||
} | ||
``` | ||
|
||
```scss | ||
@mixin foo { | ||
.baz & { | ||
color: red; | ||
} | ||
} | ||
``` |
206 changes: 206 additions & 0 deletions
206
src/rules/at-mixin-no-risky-nesting-selector/__tests__/index.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,206 @@ | ||
"use strict"; | ||
|
||
const { ruleName } = require(".."); | ||
|
||
testRule({ | ||
ruleName, | ||
config: [true], | ||
customSyntax: "postcss-scss", | ||
|
||
accept: [ | ||
{ | ||
code: ` | ||
.parent { | ||
color: blue; | ||
& .b { | ||
color: red; | ||
} | ||
} | ||
`, | ||
description: "Nested parent selector" | ||
}, | ||
{ | ||
code: ` | ||
.parent { | ||
color: blue; | ||
& .b, &.context { | ||
color: red; | ||
} | ||
} | ||
`, | ||
description: "Nested parent selector in complex selector" | ||
}, | ||
{ | ||
code: ` | ||
.bar { | ||
& .parent { | ||
color: blue; | ||
.context { | ||
color: red; | ||
} | ||
} | ||
} | ||
`, | ||
description: "Parent selector nested in another style rule" | ||
}, | ||
{ | ||
code: ` | ||
@mixin foo { | ||
&.context { | ||
color: red; | ||
} | ||
} | ||
`, | ||
description: "Parent selector in mixin" | ||
}, | ||
{ | ||
code: ` | ||
.bar { | ||
.parent { | ||
color: blue; | ||
& .context { | ||
color: red; | ||
} | ||
} | ||
} | ||
`, | ||
description: "Parent selector nested in more than one style rule" | ||
}, | ||
{ | ||
code: ` | ||
.parent { | ||
color: blue; | ||
.context & { | ||
color: red; | ||
} | ||
} | ||
`, | ||
description: "Selector ending in parent selector" | ||
}, | ||
{ | ||
code: ` | ||
.parent { | ||
color: blue; | ||
.context & .b { | ||
color: red; | ||
} | ||
} | ||
`, | ||
description: "Parent selector in the middle of complex selector" | ||
}, | ||
{ | ||
code: ` | ||
.parent { | ||
color: blue; | ||
& .b, .context & { | ||
color: red; | ||
} | ||
} | ||
`, | ||
description: "Complex selector, one ending in parent selector" | ||
}, | ||
{ | ||
code: ` | ||
@mixin foo { | ||
.parent { | ||
color: blue; | ||
& .context { | ||
color: red; | ||
} | ||
} | ||
} | ||
`, | ||
description: "Parent selector in mixin, nested, at the beginning" | ||
}, | ||
{ | ||
code: ` | ||
@mixin foo { | ||
.bar & { | ||
color: red; | ||
} | ||
} | ||
`, | ||
description: "Parent selector in mixin, at the end" | ||
}, | ||
{ | ||
code: ` | ||
@mixin foo { | ||
.parent { | ||
.bar, & { | ||
color: red; | ||
} | ||
} | ||
} | ||
`, | ||
description: "Complex selector in mixin, nested, no following class." | ||
} | ||
], | ||
|
||
reject: [ | ||
{ | ||
code: ` | ||
@mixin foo { | ||
.bar { | ||
color: blue; | ||
.baz & { | ||
color: red; | ||
} | ||
} | ||
} | ||
`, | ||
description: "Parent selector nested in selector within a mixin", | ||
message: | ||
"Unexpected nested parent selector in @mixin rule. (scss/at-mixin-no-risky-nesting-selector)", | ||
column: 11, | ||
endColumn: 12, | ||
endLine: 7, | ||
line: 5 | ||
}, | ||
{ | ||
code: ` | ||
@mixin foo { | ||
.bar { | ||
color: blue; | ||
& .qux, .baz & .quux{ | ||
color: red; | ||
} | ||
} | ||
} | ||
`, | ||
description: "Parent selector nested in complex selector within mixin", | ||
message: | ||
"Unexpected nested parent selector in @mixin rule. (scss/at-mixin-no-risky-nesting-selector)", | ||
column: 11, | ||
endColumn: 12, | ||
endLine: 7, | ||
line: 5 | ||
}, | ||
{ | ||
code: ` | ||
@mixin foo { | ||
.bar { | ||
color: blue; | ||
& .qux & { | ||
color: red; | ||
} | ||
} | ||
} | ||
`, | ||
description: "Parent selector nested in complex selector within mixin", | ||
message: | ||
"Unexpected nested parent selector in @mixin rule. (scss/at-mixin-no-risky-nesting-selector)", | ||
column: 11, | ||
endColumn: 12, | ||
endLine: 7, | ||
line: 5 | ||
} | ||
] | ||
}); |
Oops, something went wrong.