Skip to content

Commit

Permalink
Add mixin-no-risky-parent-selectors rule.
Browse files Browse the repository at this point in the history
  • Loading branch information
pamelalozano16 committed Apr 8, 2024
1 parent 26c105c commit 0ca7400
Show file tree
Hide file tree
Showing 5 changed files with 405 additions and 2 deletions.
Expand Up @@ -83,8 +83,7 @@ testRule({
line: 2,
column: 20,
message: messages.rejected("scssy"),
description:
"One file, ext not from an allowed list, space at the end."
description: "One file, ext not from an allowed list, space at the end."
},
{
code: `
Expand Down
1 change: 1 addition & 0 deletions src/rules/index.js
Expand Up @@ -56,6 +56,7 @@ const rules = {
"function-unquote-no-unquoted-strings-inside": require("./function-unquote-no-unquoted-strings-inside"),
"map-keys-quotes": require("./map-keys-quotes"),
"media-feature-value-dollar-variable": require("./media-feature-value-dollar-variable"),
"mixin-no-risky-parent-selectors": require("./mixin-no-risky-parent-selectors"),
"no-dollar-variables": require("./no-dollar-variables"),
"no-duplicate-dollar-variables": require("./no-duplicate-dollar-variables"),
"no-duplicate-mixins": require("./no-duplicate-mixins"),
Expand Down
142 changes: 142 additions & 0 deletions src/rules/mixin-no-risky-parent-selectors/README.md
@@ -0,0 +1,142 @@
# mixin-no-risky-parent-selectors

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;
}
}
```
194 changes: 194 additions & 0 deletions src/rules/mixin-no-risky-parent-selectors/__tests__/index.js
@@ -0,0 +1,194 @@
"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/mixin-no-risky-parent-selectors)"
},
{
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/mixin-no-risky-parent-selectors)"
},
{
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/mixin-no-risky-parent-selectors)"
}
]
});

0 comments on commit 0ca7400

Please sign in to comment.