From 60b3f50aab14e3e4b874a7d29441d031caede9fe Mon Sep 17 00:00:00 2001 From: rletsin Date: Mon, 19 Oct 2020 00:08:52 +0300 Subject: [PATCH 1/4] Add 'selector-attribute-name-disallowed-list' rule --- lib/rules/index.js | 3 + .../README.md | 49 ++++++++++++++ .../__tests__/index.js | 51 +++++++++++++++ .../index.js | 65 +++++++++++++++++++ 4 files changed, 168 insertions(+) create mode 100644 lib/rules/selector-attribute-name-disallowed-list/README.md create mode 100644 lib/rules/selector-attribute-name-disallowed-list/__tests__/index.js create mode 100644 lib/rules/selector-attribute-name-disallowed-list/index.js diff --git a/lib/rules/index.js b/lib/rules/index.js index a2ee0d5260..19614f3f2b 100644 --- a/lib/rules/index.js +++ b/lib/rules/index.js @@ -273,6 +273,9 @@ const rules = { 'selector-attribute-operator-whitelist': importLazy(() => require('./selector-attribute-operator-whitelist'), )(), + 'selector-attribute-name-disallowed-list': importLazy(() => + require('./selector-attribute-name-disallowed-list'), + )(), 'selector-attribute-quotes': importLazy(() => require('./selector-attribute-quotes'))(), 'selector-class-pattern': importLazy(() => require('./selector-class-pattern'))(), 'selector-combinator-allowed-list': importLazy(() => diff --git a/lib/rules/selector-attribute-name-disallowed-list/README.md b/lib/rules/selector-attribute-name-disallowed-list/README.md new file mode 100644 index 0000000000..b97b3dc5cd --- /dev/null +++ b/lib/rules/selector-attribute-name-disallowed-list/README.md @@ -0,0 +1,49 @@ +# selector-attribute-name-disallowed-list + +Specify a list of disallowed attribute names. + + +```css + [class~="foo"] {} +/** ↑ + * This name */ +``` + +## Options + +`array|string`: `["array", "of", "names"]|"name"` + +Given: + +``` +["class", "id"] +``` + +The following patterns are considered violations: + + +```css +[class*="foo"] {} +``` + + +```css +[id~="bar"] {} +``` + +The following patterns are _not_ considered violations: + + +```css +[lang~="en-us"] {} +``` + + +```css +[target="_blank"] {} +``` + + +```css +[href$=".bar"] {} +``` diff --git a/lib/rules/selector-attribute-name-disallowed-list/__tests__/index.js b/lib/rules/selector-attribute-name-disallowed-list/__tests__/index.js new file mode 100644 index 0000000000..8d4f715546 --- /dev/null +++ b/lib/rules/selector-attribute-name-disallowed-list/__tests__/index.js @@ -0,0 +1,51 @@ +'use strict'; + +const { messages, ruleName } = require('..'); + +testRule({ + ruleName, + + config: ['class', 'id'], + + 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, + }, + ], +}); diff --git a/lib/rules/selector-attribute-name-disallowed-list/index.js b/lib/rules/selector-attribute-name-disallowed-list/index.js new file mode 100644 index 0000000000..a24b780725 --- /dev/null +++ b/lib/rules/selector-attribute-name-disallowed-list/index.js @@ -0,0 +1,65 @@ +// @ts-nocheck + +'use strict'; + +const _ = require('lodash'); +const isStandardSyntaxRule = require('../../utils/isStandardSyntaxRule'); +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], + }); + + if (!validOptions) { + return; + } + + root.walkRules((rule) => { + if (!isStandardSyntaxRule(rule)) { + return; + } + + if (!rule.selector.includes('[') || !rule.selector.includes('=')) { + return; + } + + parseSelector(rule.selector, result, rule, (selectorTree) => { + selectorTree.walkAttributes((attributeNode) => { + const attributeName = attributeNode.qualifiedAttribute; + + if (!attributeName || (attributeName && !list.includes(attributeName))) { + return; + } + + report({ + message: messages.rejected(attributeName), + node: rule, + index: attributeNode.sourceIndex + attributeNode.offsetOf('attribute'), + result, + ruleName, + }); + }); + }); + }); + }; +} + +rule.primaryOptionArray = true; + +rule.ruleName = ruleName; +rule.messages = messages; +module.exports = rule; From c986fef85deeac5b4828beda4b918e35f6679f16 Mon Sep 17 00:00:00 2001 From: rletsin Date: Mon, 19 Oct 2020 00:08:52 +0300 Subject: [PATCH 2/4] Add 'selector-attribute-name-disallowed-list' rule --- lib/rules/index.js | 3 + .../README.md | 49 ++++++++++++++ .../__tests__/index.js | 51 +++++++++++++++ .../index.js | 65 +++++++++++++++++++ 4 files changed, 168 insertions(+) create mode 100644 lib/rules/selector-attribute-name-disallowed-list/README.md create mode 100644 lib/rules/selector-attribute-name-disallowed-list/__tests__/index.js create mode 100644 lib/rules/selector-attribute-name-disallowed-list/index.js diff --git a/lib/rules/index.js b/lib/rules/index.js index 8d51bfd21c..2116623d7c 100644 --- a/lib/rules/index.js +++ b/lib/rules/index.js @@ -274,6 +274,9 @@ const rules = { 'selector-attribute-operator-whitelist': importLazy(() => require('./selector-attribute-operator-whitelist'), )(), + 'selector-attribute-name-disallowed-list': importLazy(() => + require('./selector-attribute-name-disallowed-list'), + )(), 'selector-attribute-quotes': importLazy(() => require('./selector-attribute-quotes'))(), 'selector-class-pattern': importLazy(() => require('./selector-class-pattern'))(), 'selector-combinator-allowed-list': importLazy(() => diff --git a/lib/rules/selector-attribute-name-disallowed-list/README.md b/lib/rules/selector-attribute-name-disallowed-list/README.md new file mode 100644 index 0000000000..b97b3dc5cd --- /dev/null +++ b/lib/rules/selector-attribute-name-disallowed-list/README.md @@ -0,0 +1,49 @@ +# selector-attribute-name-disallowed-list + +Specify a list of disallowed attribute names. + + +```css + [class~="foo"] {} +/** ↑ + * This name */ +``` + +## Options + +`array|string`: `["array", "of", "names"]|"name"` + +Given: + +``` +["class", "id"] +``` + +The following patterns are considered violations: + + +```css +[class*="foo"] {} +``` + + +```css +[id~="bar"] {} +``` + +The following patterns are _not_ considered violations: + + +```css +[lang~="en-us"] {} +``` + + +```css +[target="_blank"] {} +``` + + +```css +[href$=".bar"] {} +``` diff --git a/lib/rules/selector-attribute-name-disallowed-list/__tests__/index.js b/lib/rules/selector-attribute-name-disallowed-list/__tests__/index.js new file mode 100644 index 0000000000..8d4f715546 --- /dev/null +++ b/lib/rules/selector-attribute-name-disallowed-list/__tests__/index.js @@ -0,0 +1,51 @@ +'use strict'; + +const { messages, ruleName } = require('..'); + +testRule({ + ruleName, + + config: ['class', 'id'], + + 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, + }, + ], +}); diff --git a/lib/rules/selector-attribute-name-disallowed-list/index.js b/lib/rules/selector-attribute-name-disallowed-list/index.js new file mode 100644 index 0000000000..a24b780725 --- /dev/null +++ b/lib/rules/selector-attribute-name-disallowed-list/index.js @@ -0,0 +1,65 @@ +// @ts-nocheck + +'use strict'; + +const _ = require('lodash'); +const isStandardSyntaxRule = require('../../utils/isStandardSyntaxRule'); +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], + }); + + if (!validOptions) { + return; + } + + root.walkRules((rule) => { + if (!isStandardSyntaxRule(rule)) { + return; + } + + if (!rule.selector.includes('[') || !rule.selector.includes('=')) { + return; + } + + parseSelector(rule.selector, result, rule, (selectorTree) => { + selectorTree.walkAttributes((attributeNode) => { + const attributeName = attributeNode.qualifiedAttribute; + + if (!attributeName || (attributeName && !list.includes(attributeName))) { + return; + } + + report({ + message: messages.rejected(attributeName), + node: rule, + index: attributeNode.sourceIndex + attributeNode.offsetOf('attribute'), + result, + ruleName, + }); + }); + }); + }); + }; +} + +rule.primaryOptionArray = true; + +rule.ruleName = ruleName; +rule.messages = messages; +module.exports = rule; From aaa97d95f94d1eca94368e5c8726ad2c35addaee Mon Sep 17 00:00:00 2001 From: rletsin Date: Mon, 19 Oct 2020 17:30:53 +0300 Subject: [PATCH 3/4] Add new rule to docs, fix linting error --- docs/user-guide/rules/list.md | 1 + .../selector-attribute-name-disallowed-list/index.js | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/user-guide/rules/list.md b/docs/user-guide/rules/list.md index fb3b6f7d1a..ea365083b4 100644 --- a/docs/user-guide/rules/list.md +++ b/docs/user-guide/rules/list.md @@ -173,6 +173,7 @@ Grouped first by the following categories and then by the [_thing_](http://apps. - [`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. - [`selector-attribute-operator-whitelist`](../../../lib/rules/selector-attribute-operator-whitelist/README.md): Specify a list of allowed attribute operators. **(deprecated)** +- [`selector-attribute-name-disallowed-list`](../../../lib/rules/selector-attribute-name-disallowed-list/README.md): Specify a list of disallowed attribute names. - [`selector-class-pattern`](../../../lib/rules/selector-class-pattern/README.md): Specify a pattern for class selectors. - [`selector-combinator-allowed-list`](../../../lib/rules/selector-combinator-allowed-list/README.md): Specify a list of allowed combinators. - [`selector-combinator-blacklist`](../../../lib/rules/selector-combinator-blacklist/README.md): Specify a list of disallowed combinators. **(deprecated)** diff --git a/lib/rules/selector-attribute-name-disallowed-list/index.js b/lib/rules/selector-attribute-name-disallowed-list/index.js index a24b780725..9f83d1d7f4 100644 --- a/lib/rules/selector-attribute-name-disallowed-list/index.js +++ b/lib/rules/selector-attribute-name-disallowed-list/index.js @@ -28,16 +28,16 @@ function rule(listInput) { return; } - root.walkRules((rule) => { - if (!isStandardSyntaxRule(rule)) { + root.walkRules((ruleNode) => { + if (!isStandardSyntaxRule(ruleNode)) { return; } - if (!rule.selector.includes('[') || !rule.selector.includes('=')) { + if (!ruleNode.selector.includes('[') || !ruleNode.selector.includes('=')) { return; } - parseSelector(rule.selector, result, rule, (selectorTree) => { + parseSelector(ruleNode.selector, result, ruleNode, (selectorTree) => { selectorTree.walkAttributes((attributeNode) => { const attributeName = attributeNode.qualifiedAttribute; @@ -47,7 +47,7 @@ function rule(listInput) { report({ message: messages.rejected(attributeName), - node: rule, + node: ruleNode, index: attributeNode.sourceIndex + attributeNode.offsetOf('attribute'), result, ruleName, From 1dbd9bb1c373a89ed96678b9a8b2289bb70794ac Mon Sep 17 00:00:00 2001 From: rletsin Date: Mon, 9 Nov 2020 23:07:44 +0200 Subject: [PATCH 4/4] Add regex as input option to selector-attribute-name-disallowed-list --- docs/user-guide/rules/list.md | 2 +- lib/rules/index.js | 6 +++--- .../README.md | 9 +++++++-- .../__tests__/index.js | 14 +++++++++++++- .../index.js | 5 +++-- 5 files changed, 27 insertions(+), 9 deletions(-) diff --git a/docs/user-guide/rules/list.md b/docs/user-guide/rules/list.md index ea365083b4..7d6f4ea482 100644 --- a/docs/user-guide/rules/list.md +++ b/docs/user-guide/rules/list.md @@ -169,11 +169,11 @@ 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. - [`selector-attribute-operator-whitelist`](../../../lib/rules/selector-attribute-operator-whitelist/README.md): Specify a list of allowed attribute operators. **(deprecated)** -- [`selector-attribute-name-disallowed-list`](../../../lib/rules/selector-attribute-name-disallowed-list/README.md): Specify a list of disallowed attribute names. - [`selector-class-pattern`](../../../lib/rules/selector-class-pattern/README.md): Specify a pattern for class selectors. - [`selector-combinator-allowed-list`](../../../lib/rules/selector-combinator-allowed-list/README.md): Specify a list of allowed combinators. - [`selector-combinator-blacklist`](../../../lib/rules/selector-combinator-blacklist/README.md): Specify a list of disallowed combinators. **(deprecated)** diff --git a/lib/rules/index.js b/lib/rules/index.js index 2116623d7c..51ef1353a8 100644 --- a/lib/rules/index.js +++ b/lib/rules/index.js @@ -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'), )(), @@ -274,9 +277,6 @@ const rules = { 'selector-attribute-operator-whitelist': importLazy(() => require('./selector-attribute-operator-whitelist'), )(), - 'selector-attribute-name-disallowed-list': importLazy(() => - require('./selector-attribute-name-disallowed-list'), - )(), 'selector-attribute-quotes': importLazy(() => require('./selector-attribute-quotes'))(), 'selector-class-pattern': importLazy(() => require('./selector-class-pattern'))(), 'selector-combinator-allowed-list': importLazy(() => diff --git a/lib/rules/selector-attribute-name-disallowed-list/README.md b/lib/rules/selector-attribute-name-disallowed-list/README.md index b97b3dc5cd..36f7a2a759 100644 --- a/lib/rules/selector-attribute-name-disallowed-list/README.md +++ b/lib/rules/selector-attribute-name-disallowed-list/README.md @@ -11,12 +11,12 @@ Specify a list of disallowed attribute names. ## Options -`array|string`: `["array", "of", "names"]|"name"` +`array|string|regex`: `["array", "of", /names/ or "regex"]|"name"|/regex/` Given: ``` -["class", "id"] +["class", "id", "/^data-/"] ``` The following patterns are considered violations: @@ -31,6 +31,11 @@ The following patterns are considered violations: [id~="bar"] {} ``` + +```css +[data-foo*="bar"] {} +``` + The following patterns are _not_ considered violations: diff --git a/lib/rules/selector-attribute-name-disallowed-list/__tests__/index.js b/lib/rules/selector-attribute-name-disallowed-list/__tests__/index.js index 8d4f715546..c321ce391d 100644 --- a/lib/rules/selector-attribute-name-disallowed-list/__tests__/index.js +++ b/lib/rules/selector-attribute-name-disallowed-list/__tests__/index.js @@ -5,7 +5,7 @@ const { messages, ruleName } = require('..'); testRule({ ruleName, - config: ['class', 'id'], + config: ['class', 'id', '/^data-/'], accept: [ { @@ -47,5 +47,17 @@ testRule({ 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, + }, ], }); diff --git a/lib/rules/selector-attribute-name-disallowed-list/index.js b/lib/rules/selector-attribute-name-disallowed-list/index.js index 9f83d1d7f4..28bf32f489 100644 --- a/lib/rules/selector-attribute-name-disallowed-list/index.js +++ b/lib/rules/selector-attribute-name-disallowed-list/index.js @@ -4,6 +4,7 @@ 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'); @@ -21,7 +22,7 @@ function rule(listInput) { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: list, - possible: [_.isString], + possible: [_.isString, _.isRegExp], }); if (!validOptions) { @@ -41,7 +42,7 @@ function rule(listInput) { selectorTree.walkAttributes((attributeNode) => { const attributeName = attributeNode.qualifiedAttribute; - if (!attributeName || (attributeName && !list.includes(attributeName))) { + if (!matchesStringOrRegExp(attributeName, list)) { return; }