Skip to content

Commit

Permalink
Add selector-attribute-name-disallowed-list (#4992)
Browse files Browse the repository at this point in the history
  • Loading branch information
rletsin committed Nov 17, 2020
1 parent d42f8da commit c36b8d0
Show file tree
Hide file tree
Showing 5 changed files with 187 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/user-guide/rules/list.md
Expand Up @@ -169,6 +169,7 @@ Grouped first by the following categories and then by the [_thing_](http://apps.

### Selector

- [`selector-attribute-name-disallowed-list`](../../../lib/rules/selector-attribute-name-disallowed-list/README.md): Specify a list of disallowed attribute names.
- [`selector-attribute-operator-allowed-list`](../../../lib/rules/selector-attribute-operator-allowed-list/README.md): Specify a list of allowed attribute operators.
- [`selector-attribute-operator-blacklist`](../../../lib/rules/selector-attribute-operator-blacklist/README.md): Specify a list of disallowed attribute operators. **(deprecated)**
- [`selector-attribute-operator-disallowed-list`](../../../lib/rules/selector-attribute-operator-disallowed-list/README.md): Specify a list of disallowed attribute operators.
Expand Down
3 changes: 3 additions & 0 deletions lib/rules/index.js
Expand Up @@ -256,6 +256,9 @@ const rules = {
'selector-attribute-brackets-space-inside': importLazy(() =>
require('./selector-attribute-brackets-space-inside'),
)(),
'selector-attribute-name-disallowed-list': importLazy(() =>
require('./selector-attribute-name-disallowed-list'),
)(),
'selector-attribute-operator-allowed-list': importLazy(() =>
require('./selector-attribute-operator-allowed-list'),
)(),
Expand Down
54 changes: 54 additions & 0 deletions lib/rules/selector-attribute-name-disallowed-list/README.md
@@ -0,0 +1,54 @@
# selector-attribute-name-disallowed-list

Specify a list of disallowed attribute names.

<!-- prettier-ignore -->
```css
[class~="foo"] {}
/** ↑
* This name */
```

## Options

`array|string|regex`: `["array", "of", /names/ or "regex"]|"name"|/regex/`

Given:

```
["class", "id", "/^data-/"]
```

The following patterns are considered violations:

<!-- prettier-ignore -->
```css
[class*="foo"] {}
```

<!-- prettier-ignore -->
```css
[id~="bar"] {}
```

<!-- prettier-ignore -->
```css
[data-foo*="bar"] {}
```

The following patterns are _not_ considered violations:

<!-- prettier-ignore -->
```css
[lang~="en-us"] {}
```

<!-- prettier-ignore -->
```css
[target="_blank"] {}
```

<!-- prettier-ignore -->
```css
[href$=".bar"] {}
```
@@ -0,0 +1,63 @@
'use strict';

const { messages, ruleName } = require('..');

testRule({
ruleName,

config: ['class', 'id', '/^data-/'],

accept: [
{
code: 'a[title] { }',
},
{
code: 'a[target="_blank"] { }',
},
{
code: 'a[href$=".pdf"] { }',
},
{
code: 'div[lang~="en-us"] { }',
},
],

reject: [
{
code: '[class*="foo"] { }',
message: messages.rejected('class'),
line: 1,
column: 2,
},
{
code: '[ class*="foo" ] { }',
message: messages.rejected('class'),
line: 1,
column: 3,
},
{
code: '[class *= "foo"] { }',
message: messages.rejected('class'),
line: 1,
column: 2,
},
{
code: '[id~="bar"] { }',
message: messages.rejected('id'),
line: 1,
column: 2,
},
{
code: '[data-foo*="bar"] { }',
message: messages.rejected('data-foo'),
line: 1,
column: 2,
},
{
code: '[ data-foo *= "bar" ] { }',
message: messages.rejected('data-foo'),
line: 1,
column: 3,
},
],
});
66 changes: 66 additions & 0 deletions lib/rules/selector-attribute-name-disallowed-list/index.js
@@ -0,0 +1,66 @@
// @ts-nocheck

'use strict';

const _ = require('lodash');
const isStandardSyntaxRule = require('../../utils/isStandardSyntaxRule');
const matchesStringOrRegExp = require('../../utils/matchesStringOrRegExp');
const parseSelector = require('../../utils/parseSelector');
const report = require('../../utils/report');
const ruleMessages = require('../../utils/ruleMessages');
const validateOptions = require('../../utils/validateOptions');

const ruleName = 'selector-attribute-name-disallowed-list';

const messages = ruleMessages(ruleName, {
rejected: (name) => `Unexpected name "${name}"`,
});

function rule(listInput) {
const list = [].concat(listInput);

return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: list,
possible: [_.isString, _.isRegExp],
});

if (!validOptions) {
return;
}

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

if (!ruleNode.selector.includes('[') || !ruleNode.selector.includes('=')) {
return;
}

parseSelector(ruleNode.selector, result, ruleNode, (selectorTree) => {
selectorTree.walkAttributes((attributeNode) => {
const attributeName = attributeNode.qualifiedAttribute;

if (!matchesStringOrRegExp(attributeName, list)) {
return;
}

report({
message: messages.rejected(attributeName),
node: ruleNode,
index: attributeNode.sourceIndex + attributeNode.offsetOf('attribute'),
result,
ruleName,
});
});
});
});
};
}

rule.primaryOptionArray = true;

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

0 comments on commit c36b8d0

Please sign in to comment.