Skip to content

Commit

Permalink
Re-instate whitelist/blacklist/requirelist rules
Browse files Browse the repository at this point in the history
The aliasing approach taken for these rule renaming presented user
experience problems [1]. To resolve these problems the preference is to
revert on the aliasing strategy and instead return to a deprecation
strategy. The first step towards this is re-instating these rules.

As per the preference of jeddy3 [2] these rules are re-instated using a copy
and paste strategy, with the expectation these will be removed in the
next major release. An approach that didn't involve copy and pasting was
previously introduced in [3] and could be looked at again if maintaining this
duplicate code proves problematic.

[1]: stylelint#4854 (comment)
[2]: stylelint#4854 (comment)
[3]: stylelint@e93e44c
  • Loading branch information
kevindew committed Aug 8, 2020
1 parent f02a588 commit 8a768ce
Show file tree
Hide file tree
Showing 84 changed files with 8,947 additions and 117 deletions.
41 changes: 0 additions & 41 deletions lib/__tests__/aliasedRules.test.js

This file was deleted.

13 changes: 0 additions & 13 deletions lib/__tests__/integration.test.js
Expand Up @@ -143,19 +143,6 @@ it('Scss integration test', () => {
});
});

it('rule aliasing integration test', () => {
return postcss()
.use(stylelint({ rules: { 'unit-blacklist': ['px'] } }))
.process('a { top: 10px; }', { from: undefined })
.then((result) => {
const error = result.messages[0];

expect(error).toBeTruthy();
expect(error.rule).toBe('unit-disallowed-list');
expect(error.text).toBe('Unexpected unit "px" (unit-disallowed-list)');
});
});

describe('integration test null option', () => {
let results;

Expand Down
52 changes: 52 additions & 0 deletions lib/rules/at-rule-blacklist/README.md
@@ -0,0 +1,52 @@
# at-rule-disallowed-list

Specify a list of disallowed at-rules.

<!-- prettier-ignore -->
```css
@keyframes name {}
/** ↑
* At-rules like this */
```

This rule was previously called, and is aliased as, `at-rule-blacklist`.

## Options

`array|string`: `["array", "of", "unprefixed", "at-rules"]|"at-rule"`

Given:

```
["extend", "keyframes"]
```

The following patterns are considered violations:

<!-- prettier-ignore -->
```css
a { @extend placeholder; }
```

<!-- prettier-ignore -->
```css
@keyframes name {
from { top: 10px; }
to { top: 20px; }
}
```

<!-- prettier-ignore -->
```css
@-moz-keyframes name {
from { top: 10px; }
to { top: 20px; }
}
```

The following patterns are _not_ considered violations:

<!-- prettier-ignore -->
```css
@import "path/to/file.css";
```
174 changes: 174 additions & 0 deletions lib/rules/at-rule-blacklist/__tests__/index.js
@@ -0,0 +1,174 @@
'use strict';

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

testRule({
ruleName,

config: ['extend', 'supports', 'keyframes'],

accept: [
{
code: 'a { color: pink; }',
description: 'Some random code.',
},
{
code: '@mixin name ($p) {}',
description: '@rule not from a disallowed list.',
},
],

reject: [
{
code: 'a { @extend %placeholder; }',
message: messages.rejected('extend'),
line: 1,
column: 5,
description: '@rule from a disallowed list, is a Sass directive.',
},
{
code: `
a {
@extend
%placeholder;
}
`,
message: messages.rejected('extend'),
line: 3,
column: 9,
description: '@rule from a disallowed list; newline after its name.',
},
{
code: `
@keyframes name {
from { top: 10px; }
to { top: 20px; }
}
`,
message: messages.rejected('keyframes'),
line: 2,
description: '@rule from a disallowed list; independent rule.',
},
{
code: `
@Keyframes name {
from { top: 10px; }
to { top: 20px; }
}
`,
message: messages.rejected('Keyframes'),
line: 2,
column: 7,
description: '@rule from a disallowed list; independent rule; messed case.',
},
{
code: `
@-moz-keyframes name {
from { top: 10px; }
to { top: 20px; }
}
`,
message: messages.rejected('-moz-keyframes'),
line: 2,
column: 7,
description: '@rule from a disallowed list; independent rule; has vendor prefix.',
},
{
code: `
@-WEBKET-KEYFRAMES name {
from { top: 10px; }
to { top: 20px; }
}
`,
message: messages.rejected('-WEBKET-KEYFRAMES'),
line: 2,
column: 7,
description: '@rule from a disallowed list; independent rule; has vendor prefix.',
},
],
});

testRule({
ruleName,

config: ['keyframes'],

accept: [
{
code: 'a { color: pink; }',
description: 'Some random code.',
},
{
code: '@mixin name ($p) {}',
description: '@rule not from a disallowed list.',
},
],

reject: [
{
code: `
@keyframes name {
from { top: 10px; }
to { top: 20px; }
}
`,
message: messages.rejected('keyframes'),
line: 2,
column: 7,
description: '@rule from a disallowed list; independent rule.',
},
{
code: `
@Keyframes name {
from { top: 10px; }
to { top: 20px; }
}
`,
message: messages.rejected('Keyframes'),
line: 2,
column: 7,
description: '@rule from a disallowed list; independent rule; messed case.',
},
{
code: `
@-moz-keyframes name {
from { top: 10px; }
to { top: 20px; }
}
`,
message: messages.rejected('-moz-keyframes'),
line: 2,
column: 7,
description: '@rule from a disallowed list; independent rule; has vendor prefix.',
},
{
code: `
@-WEBKET-KEYFRAMES name {
from { top: 10px; }
to { top: 20px; }
}
`,
message: messages.rejected('-WEBKET-KEYFRAMES'),
line: 2,
column: 7,
description: '@rule from a disallowed list; independent rule; has vendor prefix.',
},
],
});

testRule({
ruleName,
syntax: 'less',
config: ['keyframes'],

accept: [
{
code: `
.keyframes() { margin: 0; }
span { .keyframes(); }
`,
description: 'ignore Less mixin which are treated as at-rule',
},
],
});
57 changes: 57 additions & 0 deletions lib/rules/at-rule-blacklist/index.js
@@ -0,0 +1,57 @@
// @ts-nocheck

'use strict';

const _ = require('lodash');
const isStandardSyntaxAtRule = require('../../utils/isStandardSyntaxAtRule');
const postcss = require('postcss');
const report = require('../../utils/report');
const ruleMessages = require('../../utils/ruleMessages');
const validateOptions = require('../../utils/validateOptions');

const ruleName = 'at-rule-blacklist';

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

function rule(listInput) {
// To allow for just a string as a parameter (not only arrays of strings)
const list = [].concat(listInput);

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

if (!validOptions) {
return;
}

root.walkAtRules((atRule) => {
const name = atRule.name;

if (!isStandardSyntaxAtRule(atRule)) {
return;
}

if (!list.includes(postcss.vendor.unprefixed(name).toLowerCase())) {
return;
}

report({
message: messages.rejected(name),
node: atRule,
result,
ruleName,
});
});
};
}

rule.primaryOptionArray = true;

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

0 comments on commit 8a768ce

Please sign in to comment.