Skip to content

Commit cfbe772

Browse files
karlhorkyjeddy3
andauthored
Add ignoreAtRules: [] to nesting-selector-no-missing-scoping-root (#8743)
Co-authored-by: Richard Hallows <jeddy3@users.noreply.github.com>
1 parent 5d642e9 commit cfbe772

File tree

5 files changed

+128
-17
lines changed

5 files changed

+128
-17
lines changed

lib/rules/nesting-selector-no-missing-scoping-root/README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,40 @@ a {
6464
@scope (&) {}
6565
}
6666
```
67+
68+
## Optional secondary options
69+
70+
### `ignoreAtRules`
71+
72+
```json
73+
{ "ignoreAtRules": ["array", "of", "at-rules", "/regex/"] }
74+
```
75+
76+
Ignore nesting selectors within specified at-rules.
77+
78+
Given:
79+
80+
```json
81+
{
82+
"nesting-selector-no-missing-scoping-root": [
83+
true,
84+
{ "ignoreAtRules": ["--foo", "/^--bar-/"] }
85+
]
86+
}
87+
```
88+
89+
The following patterns are _not_ considered problems:
90+
91+
<!-- prettier-ignore -->
92+
```css
93+
@--foo {
94+
& {}
95+
}
96+
```
97+
98+
<!-- prettier-ignore -->
99+
```css
100+
@--bar-baz qux {
101+
& {}
102+
}
103+
```

lib/rules/nesting-selector-no-missing-scoping-root/__tests__/index.mjs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,3 +226,36 @@ testRule({
226226
},
227227
],
228228
});
229+
230+
testRule({
231+
ruleName,
232+
config: [true, { ignoreAtRules: ['--foo', '/^--bar-/'] }],
233+
234+
accept: [
235+
{
236+
code: '@--foo { & {} }',
237+
},
238+
{
239+
code: '@--bar-baz qux { & {} }',
240+
},
241+
],
242+
243+
reject: [
244+
{
245+
code: '@media all { & {} }',
246+
message: messages.rejected,
247+
line: 1,
248+
column: 14,
249+
endLine: 1,
250+
endColumn: 15,
251+
},
252+
{
253+
code: '@--baz { & {} }',
254+
message: messages.rejected,
255+
line: 1,
256+
column: 10,
257+
endLine: 1,
258+
endColumn: 11,
259+
},
260+
],
261+
});

lib/rules/nesting-selector-no-missing-scoping-root/index.cjs

Lines changed: 27 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/rules/nesting-selector-no-missing-scoping-root/index.mjs

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import { parse, walk } from 'css-tree';
22

3+
import { isRegExp, isString } from '../../utils/validateTypes.mjs';
34
import { atRuleParamIndex } from '../../utils/nodeFieldIndices.mjs';
45
import { atRuleRegexes } from '../../utils/regexes.mjs';
56
import getAtRuleParams from '../../utils/getAtRuleParams.mjs';
67
import getRuleSelector from '../../utils/getRuleSelector.mjs';
78
import isStandardSyntaxAtRule from '../../utils/isStandardSyntaxAtRule.mjs';
89
import isStandardSyntaxRule from '../../utils/isStandardSyntaxRule.mjs';
10+
import optionsMatches from '../../utils/optionsMatches.mjs';
911
import report from '../../utils/report.mjs';
1012
import ruleMessages from '../../utils/ruleMessages.mjs';
1113
import validateOptions from '../../utils/validateOptions.mjs';
@@ -21,20 +23,28 @@ const meta = {
2123
};
2224

2325
/** @type {import('stylelint').CoreRules[ruleName]} */
24-
const rule = (primary) => {
26+
const rule = (primary, secondaryOptions) => {
2527
return (root, result) => {
26-
const validOptions = validateOptions(result, ruleName, {
27-
actual: primary,
28-
possible: [true],
29-
});
28+
const validOptions = validateOptions(
29+
result,
30+
ruleName,
31+
{ actual: primary, possible: [true] },
32+
{
33+
actual: secondaryOptions,
34+
possible: {
35+
ignoreAtRules: [isString, isRegExp],
36+
},
37+
optional: true,
38+
},
39+
);
3040

3141
if (!validOptions) return;
3242

3343
root.walkRules(/&/, (ruleNode) => {
3444
if (!isStandardSyntaxRule(ruleNode)) return;
3545

3646
// Check if the rule is nested within a scoping root
37-
if (hasValidScopingRoot(ruleNode)) return;
47+
if (hasValidScopingRoot(ruleNode, secondaryOptions)) return;
3848

3949
let ast;
4050

@@ -56,7 +66,7 @@ const rule = (primary) => {
5666
if (!atRule.params.includes('&')) return;
5767

5868
// Only check @scope at-rules that don't have a parent scoping context
59-
if (hasValidScopingRoot(atRule)) return;
69+
if (hasValidScopingRoot(atRule, secondaryOptions)) return;
6070

6171
let ast;
6272

@@ -109,9 +119,10 @@ const rule = (primary) => {
109119
/**
110120
* Check if a node has a valid scoping root
111121
* @param {import('postcss').Rule | import('postcss').AtRule} node
122+
* @param {object} secondaryOptions
112123
* @returns {boolean}
113124
*/
114-
function hasValidScopingRoot(node) {
125+
function hasValidScopingRoot(node, secondaryOptions) {
115126
let current = node.parent;
116127

117128
while (current) {
@@ -125,6 +136,14 @@ function hasValidScopingRoot(node) {
125136
return true;
126137
}
127138

139+
// If we find an ignored at-rule, it provides a scoping root
140+
if (
141+
current.type === 'atrule' &&
142+
optionsMatches(secondaryOptions, 'ignoreAtRules', current.name)
143+
) {
144+
return true;
145+
}
146+
128147
// If we reach the root without finding a scoping root
129148
if (current.type === 'root') {
130149
return false;

types/stylelint/index.d.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -783,7 +783,10 @@ declare namespace stylelint {
783783
RejectedMessage<[name: string]>
784784
>;
785785
'named-grid-areas-no-invalid': CoreRule<true>;
786-
'nesting-selector-no-missing-scoping-root': CoreRule<true>;
786+
'nesting-selector-no-missing-scoping-root': CoreRule<
787+
true,
788+
{ ignoreAtRules: OneOrMany<StringOrRegex> }
789+
>;
787790
'no-descending-specificity': CoreRule<
788791
true,
789792
{ ignore: OneOrMany<'selectors-within-list'> },

0 commit comments

Comments
 (0)