Skip to content

Commit

Permalink
Add ignorePseudoClasses: [] to max-nesting-depth (#5620)
Browse files Browse the repository at this point in the history
Co-authored-by: Richard Hallows <jeddy3@users.noreply.github.com>
  • Loading branch information
lachieh and jeddy3 committed Oct 27, 2021
1 parent b31cd34 commit 4f8eea4
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 35 deletions.
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;
}
}
}
```
58 changes: 45 additions & 13 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; }}}}',
code: 'a { &:hover { b { c { top: 0; }}}}',
message: messages.expected(1),
},
{
code: '.a { .b { &:hover { &:focus { &:otherone { .c { top: 0; }}}}}}',
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 { b { &:hover, c { top: 0; }}}',
message: messages.expected(1),
},
],
});

testRule({
ruleName,
config: [1, { ignorePseudoClasses: ['hover', '/^--custom-.*$/'] }],

accept: [
{
code: 'a { &:hover { b { top: 0; } } }',
},
{
code: 'a { &:hover, &:--custom-pseudo { b { top: 0; } } }',
},
],

reject: [
{
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; } } }',
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
34 changes: 31 additions & 3 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 @@ -105,7 +108,23 @@ const rule = (primary, secondaryOptions) => {
const normalized = parser().processSync(selector, { lossless: false });
const selectors = normalized.split(',');

return selectors.every((sel) => sel.startsWith('&:') && sel[2] !== ':');
return selectors.every((sel) => extractPseudoRule(sel));
}

/**
* @param {string[]} selectors
* @returns {boolean}
*/
function containsIgnoredPseudoClassesOnly(selectors) {
if (!(secondaryOptions && secondaryOptions.ignorePseudoClasses)) return false;

return selectors.every((selector) => {
const pseudoRule = extractPseudoRule(selector);

if (!pseudoRule) return false;

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

if (
Expand All @@ -114,7 +133,8 @@ const rule = (primary, secondaryOptions) => {
node.every((child) => !isDeclaration(child))) ||
(optionsMatches(secondaryOptions, 'ignore', 'pseudo-classes') &&
isRule(node) &&
containsPseudoClassesOnly(node.selector))
containsPseudoClassesOnly(node.selector)) ||
(isRule(node) && containsIgnoredPseudoClassesOnly(node.selectors))
) {
return nestingDepth(parent, level);
}
Expand All @@ -127,6 +147,14 @@ const rule = (primary, secondaryOptions) => {
}
};

/**
* @param {string} selector
* @returns {string | undefined}
*/
function extractPseudoRule(selector) {
return selector.startsWith('&:') && selector[2] !== ':' ? selector.substr(2) : undefined;
}

rule.ruleName = ruleName;
rule.messages = messages;
module.exports = rule;

0 comments on commit 4f8eea4

Please sign in to comment.