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

46 changes: 46 additions & 0 deletions lib/rules/selector-disallowed-list/README.md
Expand Up @@ -66,3 +66,49 @@ a
```css
a[href] {}
```

## Optional secondary options

### `iterateThroughList: 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"]`
mattmanuel90 marked this conversation as resolved.
Show resolved Hide resolved

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'], iterateThroughList: 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
72 changes: 54 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,13 +18,25 @@ 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>, { iterateThroughList: 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'],
iterateThroughList: [isBoolean],
},
optional: true,
},
);

if (!validOptions) {
return;
Expand All @@ -34,21 +47,44 @@ const rule = (primary) => {
return;
}

const { selector, raws } = ruleNode;
if (optionsMatches(secondaryOptions, 'ignore', 'inside-block')) {
mattmanuel90 marked this conversation as resolved.
Show resolved Hide resolved
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;
const iterateThroughList = secondaryOptions && secondaryOptions.iterateThroughList;

if (iterateThroughList) {
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