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 ignore: ["inside-block"] and splitList to selector-disallowed-list #6334

5 changes: 5 additions & 0 deletions .changeset/unlucky-dolphins-fetch.md
@@ -0,0 +1,5 @@
---
"stylelint": minor
---

Added: `ignore: ["inside-block"]` and `splitList` to `selector-disallowed-list`
47 changes: 47 additions & 0 deletions lib/rules/selector-disallowed-list/README.md
Expand Up @@ -66,3 +66,50 @@ a
```css
a[href] {}
```

## Optional secondary options

### `splitList: true | false` (default: `false`)

Iterate through each individual selector in a selector list.
jeddy3 marked this conversation as resolved.
Show resolved Hide resolved

For example, with `true`.

Given:

```json
[".foo"]
```

The following pattern is considered a problem:

<!-- prettier-ignore -->
```css
.bar, .foo {}
```

The following pattern is _not_ considered a problem:

<!-- prettier-ignore -->
```css
.bar .foo {}
mattmanuel90 marked this conversation as resolved.
Show resolved Hide resolved
```

### `ignore: ["inside-block"]`

Ignore selectors that are inside a block.

Given:

```json
[".foo"]
```

The following pattern is _not_ considered a problem:

<!-- prettier-ignore -->
```css
.bar {
.foo {}
}
```
57 changes: 57 additions & 0 deletions lib/rules/selector-disallowed-list/__tests__/index.js
Expand Up @@ -66,6 +66,63 @@ testRule({
],
});

testRule({
ruleName,
config: [/^\.(foo)[,\s]?(?!\w).*$/, { ignore: ['inside-block'], splitList: true }],
accept: [
{
code: 'a.foo {}',
},
{
code: 'a\n>\n.foo {}',
},
{
code: '.bar > a > .foo {}',
},
{
code: '.bar { .foo {} }',
},
{
code: '.fooo {}',
},
],

reject: [
{
code: '.foo {}',
message: messages.rejected('.foo'),
line: 1,
column: 1,
endLine: 1,
endColumn: 5,
},
{
code: '.foo > .bar {}',
message: messages.rejected('.foo > .bar'),
line: 1,
column: 1,
endLine: 1,
endColumn: 12,
},
{
code: '.foo, .bar {}',
message: messages.rejected('.foo'),
line: 1,
column: 1,
endLine: 1,
endColumn: 5,
},
{
code: '.bar, .foo {}',
message: messages.rejected('.foo'),
line: 1,
column: 7,
endColumn: 11,
endLine: 1,
},
],
});

testRule({
ruleName,
config: /\.foo[^>]*>.*\.bar/,
Expand Down
73 changes: 55 additions & 18 deletions lib/rules/selector-disallowed-list/index.js
Expand Up @@ -3,9 +3,10 @@
const isStandardSyntaxRule = require('../../utils/isStandardSyntaxRule');
const matchesStringOrRegExp = require('../../utils/matchesStringOrRegExp');
const report = require('../../utils/report');
const optionsMatches = require('../../utils/optionsMatches');
const ruleMessages = require('../../utils/ruleMessages');
const validateOptions = require('../../utils/validateOptions');
const { isRegExp, isString } = require('../../utils/validateTypes');
const { isRegExp, isString, isBoolean } = require('../../utils/validateTypes');

const ruleName = 'selector-disallowed-list';

Expand All @@ -17,38 +18,74 @@ const meta = {
url: 'https://stylelint.io/user-guide/rules/list/selector-disallowed-list',
};

/** @type {import('stylelint').Rule<string | RegExp | Array<string | RegExp>>} */
const rule = (primary) => {
/** @type {import('stylelint').Rule<string | RegExp | Array<string | RegExp>, { splitList: boolean, ignore: string[] }>} */
const rule = (primary, secondaryOptions) => {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: primary,
possible: [isString, isRegExp],
});
const validOptions = validateOptions(
result,
ruleName,
{
actual: primary,
possible: [isString, isRegExp],
},
{
actual: secondaryOptions,
possible: {
ignore: ['inside-block'],
splitList: [isBoolean],
},
optional: true,
},
);

if (!validOptions) {
return;
}

const ignoreInsideBlock = optionsMatches(secondaryOptions, 'ignore', 'inside-block');
const splitList = secondaryOptions && secondaryOptions.splitList;

root.walkRules((ruleNode) => {
if (!isStandardSyntaxRule(ruleNode)) {
return;
}

const { selector, raws } = ruleNode;
if (ignoreInsideBlock) {
const { parent } = ruleNode;
const isInsideBlock = parent && parent.type !== 'root';

if (!matchesStringOrRegExp(selector, primary)) {
return;
if (isInsideBlock) {
return;
}
}

const word = (raws.selector && raws.selector.raw) || selector;
if (splitList) {
ruleNode.selectors.forEach((selector) => {
if (matchesStringOrRegExp(selector, primary)) {
report({
result,
ruleName,
message: messages.rejected(selector),
node: ruleNode,
word: selector,
});
}
});
} else {
const { selector, raws } = ruleNode;

report({
result,
ruleName,
message: messages.rejected(selector),
node: ruleNode,
word,
});
if (matchesStringOrRegExp(selector, primary)) {
const word = (raws.selector && raws.selector.raw) || selector;

report({
result,
ruleName,
message: messages.rejected(selector),
node: ruleNode,
word,
});
}
}
});
};
};
Expand Down