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 rule-selector-property-disallowed-list #5679

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
47b9f56
Add new rule-selector-property-disallowed-list rule
doing-art Aug 22, 2021
65515a8
Add typecheck
doing-art Oct 7, 2021
481a94b
Fix typecheck issue
doing-art Oct 7, 2021
efdd723
Update lib/rules/rule-selector-property-disallowed-list/README.md
doing-art Oct 15, 2021
a772f27
Update lib/rules/rule-selector-property-disallowed-list/README.md
doing-art Oct 15, 2021
4116e5c
Update lib/rules/rule-selector-property-disallowed-list/README.md
doing-art Oct 15, 2021
11b2741
Update lib/rules/rule-selector-property-disallowed-list/README.md
doing-art Oct 15, 2021
4a627db
Update lib/rules/rule-selector-property-disallowed-list/README.md
doing-art Oct 15, 2021
5f61d43
Update lib/rules/rule-selector-property-disallowed-list/README.md
doing-art Oct 15, 2021
e2863f2
Update lib/rules/rule-selector-property-disallowed-list/README.md
doing-art Oct 15, 2021
6be7d4a
Update lib/rules/rule-selector-property-disallowed-list/README.md
doing-art Oct 15, 2021
264893e
Update lib/rules/rule-selector-property-disallowed-list/README.md
doing-art Oct 15, 2021
538f8fe
Update lib/rules/rule-selector-property-disallowed-list/README.md
doing-art Oct 15, 2021
b67c6b8
Update lib/rules/rule-selector-property-disallowed-list/index.js
doing-art Oct 15, 2021
acb229a
Update lib/rules/rule-selector-property-disallowed-list/__tests__/ind…
doing-art Oct 15, 2021
f42f492
Update tests for rule-selector-property-disallowed-list rule
doing-art Oct 15, 2021
584c8c7
Support multiple errors for several properties in one block
doing-art Oct 31, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/user-guide/rules/list.md
Expand Up @@ -194,6 +194,10 @@ Grouped first by the following categories and then by the [_thing_](http://apps.
- [`selector-pseudo-element-colon-notation`](../../../lib/rules/selector-pseudo-element-colon-notation/README.md): Specify single or double colon notation for applicable pseudo-elements (Autofixable).
- [`selector-pseudo-element-disallowed-list`](../../../lib/rules/selector-pseudo-element-disallowed-list/README.md): Specify a list of disallowed pseudo-element selectors.

### Rules

- [`rule-selector-property-disallowed-list`](../../../lib/rules/rule-selector-property-disallowed-list/README.md): Specify a list of disallowed properties for selectors within rules.

### Media feature

- [`media-feature-name-allowed-list`](../../../lib/rules/media-feature-name-allowed-list/README.md): Specify a list of allowed media feature names.
Expand Down
3 changes: 3 additions & 0 deletions lib/rules/index.js
Expand Up @@ -235,6 +235,9 @@ const rules = {
'property-no-unknown': importLazy(() => require('./property-no-unknown'))(),
'property-no-vendor-prefix': importLazy(() => require('./property-no-vendor-prefix'))(),
'rule-empty-line-before': importLazy(() => require('./rule-empty-line-before'))(),
'rule-selector-property-disallowed-list': importLazy(() =>
require('./rule-selector-property-disallowed-list'),
)(),
'selector-attribute-brackets-space-inside': importLazy(() =>
require('./selector-attribute-brackets-space-inside'),
)(),
Expand Down
61 changes: 61 additions & 0 deletions lib/rules/rule-selector-property-disallowed-list/README.md
@@ -0,0 +1,61 @@
# rule-selector-property-disallowed-list

Specify a list of disallowed properties for selectors within rules.

<!-- prettier-ignore -->
```css
a { color: red; }
/** ↑ ↑
* Selector and property name */
```

## Options

`object`: `{ "selector": ["array", "of", "properties"]`

If a selector name is surrounded with `"/"` (e.g. `"/anchor/"`), it is interpreted as a regular expression. This allows, for example, easy targeting of all the potential anchors: `/anchor/` will match `.anchor`, `[data-anchor]`, etc.

The same goes for properties. Keep in mind that a regular expression value is matched against the entire property name, not specific parts of it. For example, a value like `"animation-duration"` will _not_ match `"/^duration/"` (notice beginning of the line boundary) but _will_ match `"/duration/"`.

Given:

```json
{
"a": ["color", "/margin/"],
"/foo/": ["/size/"]
}
```

The following patterns are considered problems:

<!-- prettier-ignore -->
```css
a { color: red; }
```

<!-- prettier-ignore -->
```css
a { margin-top: 0px; }
```

<!-- prettier-ignore -->
```css
html[data-foo] { font-size: 1px; }
```

The following patterns are _not_ considered problems:

<!-- prettier-ignore -->
```css
a { background: red; }
```

<!-- prettier-ignore -->
```css
a { padding-top: 0px; }
```

<!-- prettier-ignore -->
```css
html[data-foo] { color: red; }
```
@@ -0,0 +1,74 @@
'use strict';

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

testRule({
ruleName,
config: {
a: ['color', '/margin/'],
'/foo/': ['/size/'],
},

accept: [
{
code: 'a { background: red; }',
},
{
code: 'a { padding-top: 0px; }',
},
{
code: 'a { background-color: red; }',
},
{
code: 'a[href="#"] { color: red; }',
},
{
code: 'html.foo { color: red; }',
},
{
code: 'html[data-foo] { color: red; }',
},
],

reject: [
{
code: 'a { color: red; }',
message: messages.rejected('color', 'a'),
line: 1,
column: 5,
},
{
code: 'a { background: red; color: red; }',
message: messages.rejected('color', 'a'),
line: 1,
column: 22,
},
{
code: 'a { margin-top: 0px; }',
message: messages.rejected('margin-top', 'a'),
line: 1,
column: 5,
},
{
code: 'a { color: red; margin-top: 0px; }',
warnings: [
{ message: messages.rejected('color', 'a'), line: 1, column: 5 },
{ message: messages.rejected('margin-top', 'a'), line: 1, column: 17 },
],
line: 1,
column: 5,
},
{
code: '[data-foo] { font-size: 1rem; }',
message: messages.rejected('font-size', '[data-foo]'),
line: 1,
column: 14,
},
{
code: 'html[data-foo] { font-size: 1px; }',
message: messages.rejected('font-size', 'html[data-foo]'),
line: 1,
column: 18,
},
],
});
66 changes: 66 additions & 0 deletions lib/rules/rule-selector-property-disallowed-list/index.js
@@ -0,0 +1,66 @@
'use strict';

const isStandardSyntaxRule = require('../../utils/isStandardSyntaxRule');
const matchesStringOrRegExp = require('../../utils/matchesStringOrRegExp');
const report = require('../../utils/report');
const ruleMessages = require('../../utils/ruleMessages');
const validateOptions = require('../../utils/validateOptions');
const { isPlainObject } = require('is-plain-object');

const ruleName = 'rule-selector-property-disallowed-list';

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

/** @type {import('stylelint').Rule} */
const rule = (primary) => {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: primary,
possible: [isPlainObject],
});

if (!validOptions) {
return;
}

const selectors = Object.keys(primary);

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

const selectorKey = selectors.find((selector) =>
matchesStringOrRegExp(ruleNode.selector, selector),
);

if (!selectorKey) {
return;
}

const disallowedProperties = primary[selectorKey];

for (const node of ruleNode.nodes) {
const isDisallowedProperty =
node.type === 'decl' && matchesStringOrRegExp(node.prop, disallowedProperties);

if (isDisallowedProperty) {
report({
message: messages.rejected(node.prop, ruleNode.selector),
node,
result,
ruleName,
});
}
}
});
};
};

rule.primaryOptionArray = true;
doing-art marked this conversation as resolved.
Show resolved Hide resolved

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