Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ignorePseudoClasses: [] to max-nesting-depth #5620

Merged
merged 13 commits into from Oct 27, 2021
96 changes: 77 additions & 19 deletions lib/rules/max-nesting-depth/README.md
Expand Up @@ -170,8 +170,8 @@ The following patterns are considered problems:

<!-- prettier-ignore -->
```css
.a {
.b { /* 1 */
a {
b { /* 1 */
.c { /* 2 */
top: 0;
}
Expand All @@ -181,9 +181,9 @@ The following patterns are considered problems:

<!-- prettier-ignore -->
```css
.a {
a {
&:hover { /* ignored */
.b { /* 1 */
b { /* 1 */
.c { /* 2 */
top: 0;
}
Expand All @@ -194,8 +194,8 @@ The following patterns are considered problems:

<!-- prettier-ignore -->
```css
.a {
.b { /* 1 */
a {
b { /* 1 */
&::selection { /* 2 */
color: #64FFDA;
}
Expand All @@ -205,8 +205,8 @@ The following patterns are considered problems:

<!-- prettier-ignore -->
```css
.a {
.b { /* 1 */
a {
b { /* 1 */
&:hover, .c { /* 2 */
top: 0;
}
Expand All @@ -216,12 +216,12 @@ The following patterns are considered problems:

The following patterns are _not_ considered problems:

As all of the following pseudoclasses rules would have a nesting depth of just 1.
As all of the following pseudo-classes rules would have a nesting depth of just 1.

<!-- prettier-ignore -->
```css
.a {
.b { /* 1 */
a {
b { /* 1 */
&:hover { /* ignored */
top: 0;
}
Expand All @@ -231,8 +231,8 @@ As all of the following pseudoclasses rules would have a nesting depth of just 1

<!-- prettier-ignore -->
```css
.a {
.b { /* 1 */
a {
b { /* 1 */
&:nest {
&:nest-lvl2 { /* ignored */
top: 0;
Expand All @@ -244,9 +244,9 @@ As all of the following pseudoclasses rules would have a nesting depth of just 1

<!-- prettier-ignore -->
```css
.a {
a {
&:hover { /* ignored */
.b { /* 1 */
b { /* 1 */
top: 0;
}
}
Expand All @@ -255,11 +255,11 @@ As all of the following pseudoclasses rules would have a nesting depth of just 1

<!-- prettier-ignore -->
```css
.a {
a {
&:nest { /* ignored */
&:nest-lvl2 { /* ignored */
top: 0;
.b { /* 1 */
b { /* 1 */
bottom: 0;
}
}
Expand All @@ -269,8 +269,8 @@ As all of the following pseudoclasses rules would have a nesting depth of just 1

<!-- prettier-ignore -->
```css
.a {
.b { /* 1 */
a {
b { /* 1 */
&:hover, &:focus { /* ignored */
top: 0;
}
Expand Down Expand Up @@ -353,3 +353,61 @@ a {
}
}
```

### `ignorePseudoClasses: ["/regex/", /regex/, "string"]`

Ignore the specified pseudo-classes.

For example, with `1` and given:

```json
["hover", "^focus-"]
```

The following patterns are _not_ considered problems:

<!-- prettier-ignore -->
```css
a {
&:hover { /* ignored */
b { /* 1 */
top: 0;
}
}
}
```

<!-- prettier-ignore -->
```css
a {
&:hover, &:active { /* ignored */
b { /* 1 */
top: 0;
}
}
}
```

The following patterns are considered problems:

<!-- prettier-ignore -->
```css
a {
&:visited { /* 1 */
b { /* 2 */
top: 0;
}
}
}
```

<!-- prettier-ignore -->
```css
a {
&:hover, &:visited { /* 1 */
b { /* 2 */
top: 0;
}
}
}
```
60 changes: 46 additions & 14 deletions lib/rules/max-nesting-depth/__tests__/index.js
Expand Up @@ -117,51 +117,83 @@ testRule({

accept: [
{
code: '.a { .b { top: 0; }}',
code: 'a { b { top: 0; }}',
},
{
code: '.a { .b { &:hover { top: 0; }}}',
code: 'a { b { &:hover { top: 0; }}}',
},
{
code: '.a { .b { &:nest { &:nest-lvl2 { top: 0; }}}}',
code: 'a { b { &:nest { &:nest-lvl2 { top: 0; }}}}',
},
{
code: '.a { &:hover { .b { top: 0; }}}',
code: 'a { &:hover { b { top: 0; }}}',
},
{
code: '.a { .b { &:hover { &:focus { &:otherone { top: 0; }}}}}',
code: 'a { b { &:hover { &:focus { &:otherone { top: 0; }}}}}',
},
{
code: '.a { &:nest { &:nest-lvl2 { top: 0; .b { bottom: 0; }}}}',
code: 'a { &:nest { &:nest-lvl2 { top: 0; b { bottom: 0; }}}}',
},
{
code: '.a { .b { &:hover .c { top: 0; }}}',
code: 'a { b { &:hover .c { top: 0; }}}',
},
{
code: '.a { .b { &:hover, &:focus { top: 0; }}}',
code: 'a { b { &:hover, &:focus { top: 0; }}}',
},
],

reject: [
{
code: '.a { .b { .c { top: 0; }}}',
code: 'a { b { .c { top: 0; }}}',
message: messages.expected(1),
},
{
code: 'a { &:hover { b { .c { top: 0; }}}}',
message: messages.expected(1),
},
{
code: 'a { b { &:hover { &:focus { &:otherone { .c { top: 0; }}}}}}',
message: messages.expected(1),
},
{
code: 'a { b { &::selection { color: #64FFDA; }}}',
message: messages.expected(1),
},
{
code: '.a { &:hover { .b { .c { top: 0; }}}}',
code: 'a { b { &:hover, .c { top: 0; }}}',
message: messages.expected(1),
},
],
});

testRule({
ruleName,
config: [1, { ignorePseudoClasses: ['hover', '/^custom-.*$/'] }],
lachieh marked this conversation as resolved.
Show resolved Hide resolved

accept: [
{
code: 'a { &:hover { b { top: 0; } } }',
},
{
code: 'a { &:hover, &:custom-pseudo { b { top: 0; } } }',
lachieh marked this conversation as resolved.
Show resolved Hide resolved
},
],

reject: [
{
code: '.a { .b { &:hover { &:focus { &:otherone { .c { top: 0; }}}}}}',
code: 'a { &:visited { b { top: 0; } } }',
message: messages.expected(1),
description: 'pseudo-class not ignored',
},
{
code: '.a { .b { &::selection { color: #64FFDA; }}}',
code: 'a { &:custom-pseudo, &:visited { b { top: 0; } } }',
lachieh marked this conversation as resolved.
Show resolved Hide resolved
message: messages.expected(1),
description: 'ignored pseudo-class alongside pseudo-class',
},
{
code: '.a { .b { &:hover, .c { top: 0; }}}',
code: 'a { &:hover, b { .c { top: 0; } } }',
message: messages.expected(1),
description: 'pseudo-class alongside class',
},
],
});
Expand Down Expand Up @@ -227,7 +259,7 @@ testRule({

accept: [
{
code: '.foo { .bar { margin: { bottom: 0; } } }',
code: '.foo { bar { margin: { bottom: 0; } } }',
lachieh marked this conversation as resolved.
Show resolved Hide resolved
description: 'SCSS nested properties',
},
],
Expand Down
25 changes: 23 additions & 2 deletions lib/rules/max-nesting-depth/index.js
Expand Up @@ -25,7 +25,7 @@ const rule = (primary, secondaryOptions) => {
isAtRule(node) && optionsMatches(secondaryOptions, 'ignoreAtRules', node.name);

return (root, result) => {
validateOptions(
const validOptions = validateOptions(
result,
ruleName,
{
Expand All @@ -38,10 +38,13 @@ const rule = (primary, secondaryOptions) => {
possible: {
ignore: ['blockless-at-rules', 'pseudo-classes'],
ignoreAtRules: [isString, isRegExp],
ignorePseudoClasses: [isString, isRegExp],
},
},
);

if (!validOptions) return;

root.walkRules(checkStatement);
root.walkAtRules(checkStatement);

Expand Down Expand Up @@ -108,13 +111,31 @@ const rule = (primary, secondaryOptions) => {
return selectors.every((sel) => sel.startsWith('&:') && sel[2] !== ':');
}

/**
* @param {string[]} selectors
* @returns {boolean}
*/
function containsIgnoredPseudoClassesOnly(selectors) {
return selectors.every((selector) => {
const pseudoRule = selector.startsWith('&:') && selector[2] !== ':' && selector.substr(2);
lachieh marked this conversation as resolved.
Show resolved Hide resolved

if (!pseudoRule) return false;

return optionsMatches(secondaryOptions, 'ignorePseudoClasses', pseudoRule);
});
}

if (
(optionsMatches(secondaryOptions, 'ignore', 'blockless-at-rules') &&
isAtRule(node) &&
node.every((child) => !isDeclaration(child))) ||
(optionsMatches(secondaryOptions, 'ignore', 'pseudo-classes') &&
isRule(node) &&
containsPseudoClassesOnly(node.selector))
containsPseudoClassesOnly(node.selector)) ||
(isRule(node) &&
secondaryOptions &&
secondaryOptions.ignorePseudoClasses &&
containsIgnoredPseudoClassesOnly(node.selectors))
lachieh marked this conversation as resolved.
Show resolved Hide resolved
) {
return nestingDepth(parent, level);
}
Expand Down