Skip to content

Commit

Permalink
Add annotation-no-unknown (#6155)
Browse files Browse the repository at this point in the history
Co-authored-by: Richard Hallows <jeddy3@users.noreply.github.com>
Co-authored-by: Masafumi Koba <473530+ybiquitous@users.noreply.github.com>
  • Loading branch information
3 people committed Jun 21, 2022
1 parent 7c19b66 commit a3e655d
Show file tree
Hide file tree
Showing 5 changed files with 209 additions and 0 deletions.
4 changes: 4 additions & 0 deletions docs/user-guide/rules/list.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ Within each cateogory, the rules are grouped by the [_thing_](http://apps.workfl

## Avoid errors

### Annotation

- [`annotation-no-unknown`](../../../lib/rules/annotation-no-unknown/README.md): Disallow unknown annotations.

### Color

- [`color-no-invalid-hex`](../../../lib/rules/color-no-invalid-hex/README.md): Disallow invalid hex colors.
Expand Down
60 changes: 60 additions & 0 deletions lib/rules/annotation-no-unknown/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# annotation-no-unknown

Disallow unknown annotations.

<!-- prettier-ignore -->
```css
a { color: green !imprtant; }
/** ↑
* This annotation */
```

This rule considers annotations defined in the CSS Specifications, up to and including Editor's Drafts, to be known.

## Options

### `true`

The following pattern is considered a problem:

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

The following pattern is _not_ considered problem:

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

## Optional secondary options

### `ignoreAnnotations: ["/regex/", /regex/, "string"]`

Given:

```json
["/^--foo-/", "--bar"]
```

The following patterns are _not_ considered problems:

<!-- prettier-ignore -->
```css
a {
color: green !--foo--bar;
}
```

<!-- prettier-ignore -->
```css
a {
color: green !--bar;
}
```
58 changes: 58 additions & 0 deletions lib/rules/annotation-no-unknown/__tests__/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
'use strict';

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

testRule({
ruleName,
config: [
true,
{
ignoreAnnotations: ['/^--foo-/', '--bar'],
},
],

accept: [
{
code: 'a { color: green !important; }',
},
{
code: 'a { color: green !IMPORTANT; }',
},
{
code: 'a { color: green !ImPoRtAnT; }',
},
{
code: 'a { color: green !--foo--bar; }',
},
{
code: 'a { color: green !--bar; }',
},
],

reject: [
{
code: 'a { color: green !imprtant }',
message: messages.rejected('!imprtant'),
line: 1,
column: 18,
endLine: 1,
endColumn: 27,
},
{
code: 'a { color: green !IMPRTANT }',
message: messages.rejected('!IMPRTANT'),
line: 1,
column: 18,
endLine: 1,
endColumn: 27,
},
{
code: 'a { color: green !ImPrTaNt }',
message: messages.rejected('!ImPrTaNt'),
line: 1,
column: 18,
endLine: 1,
endColumn: 27,
},
],
});
86 changes: 86 additions & 0 deletions lib/rules/annotation-no-unknown/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
'use strict';

const valueParser = require('postcss-value-parser');

const getDeclarationValue = require('../../utils/getDeclarationValue');
const optionsMatches = require('../../utils/optionsMatches');
const report = require('../../utils/report');
const ruleMessages = require('../../utils/ruleMessages');
const validateOptions = require('../../utils/validateOptions');
const { isRegExp, isString } = require('../../utils/validateTypes');

const ruleName = 'annotation-no-unknown';

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

const meta = {
url: 'https://stylelint.io/user-guide/rules/list/annotation-no-unknown',
};

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

if (!validOptions) {
return;
}

root.walkDecls(checkStatement);

/**
* @param {import('postcss').Declaration} decl
*/
function checkStatement(decl) {
if (decl.important) return;

if (!decl.value.includes('!')) return;

const parsedValue = valueParser(getDeclarationValue(decl));

parsedValue.walk((node) => {
if (!isAnnotation(node)) return;

const value = node.value;
const tokenValue = value.slice(1);

if (optionsMatches(secondaryOptions, 'ignoreAnnotations', tokenValue)) {
return;
}

report({
message: messages.rejected(value),
node: decl,
result,
ruleName,
word: value,
});
});
}

/**
* @param {valueParser.Node} node
*/
function isAnnotation(node) {
return node.type === 'word' && node.value.startsWith('!');
}
};
};

rule.ruleName = ruleName;
rule.messages = messages;
rule.meta = meta;
module.exports = rule;
1 change: 1 addition & 0 deletions lib/rules/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const importLazy = _importLazy(require);
/** @type {typeof import('stylelint').rules} */
const rules = {
'alpha-value-notation': importLazy('./alpha-value-notation'),
'annotation-no-unknown': importLazy('./annotation-no-unknown'),
'at-rule-allowed-list': importLazy('./at-rule-allowed-list'),
'at-rule-disallowed-list': importLazy('./at-rule-disallowed-list'),
'at-rule-empty-line-before': importLazy('./at-rule-empty-line-before'),
Expand Down

0 comments on commit a3e655d

Please sign in to comment.