Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add keyframe-block-no-duplicate-selectors (#6024)
Co-authored-by: Richard Hallows <jeddy3@users.noreply.github.com> Co-authored-by: Masafumi Koba <473530+ybiquitous@users.noreply.github.com>
- Loading branch information
1 parent
3cc3d2b
commit 08df98c
Showing
5 changed files
with
187 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
# keyframe-block-no-duplicate-selectors | ||
|
||
Disallow duplicate selectors within keyframe blocks. | ||
|
||
<!-- prettier-ignore --> | ||
```css | ||
@keyframes foo { 0% {} 0% {} } | ||
/** ↑ | ||
* This duplicate selector */ | ||
``` | ||
|
||
This rule is case-insensitive. | ||
|
||
## Options | ||
|
||
### `true` | ||
|
||
The following patterns are considered problems: | ||
|
||
<!-- prettier-ignore --> | ||
```css | ||
@keyframes foo { 0% {} 0% {} } | ||
``` | ||
|
||
<!-- prettier-ignore --> | ||
```css | ||
@keyframes foo { from {} from {} } | ||
``` | ||
|
||
<!-- prettier-ignore --> | ||
```css | ||
@keyframes foo { from {} FROM {} } | ||
``` | ||
|
||
The following patterns are _not_ considered problems: | ||
|
||
<!-- prettier-ignore --> | ||
```css | ||
@keyframes foo { 0% {} 100% {} } | ||
``` | ||
|
||
<!-- prettier-ignore --> | ||
```css | ||
@keyframes foo { from {} to {} } | ||
``` |
73 changes: 73 additions & 0 deletions
73
lib/rules/keyframe-block-no-duplicate-selectors/__tests__/index.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
'use strict'; | ||
|
||
const { messages, ruleName } = require('..'); | ||
|
||
testRule({ | ||
ruleName, | ||
config: [true], | ||
|
||
accept: [ | ||
{ | ||
code: '@keyframes foo { 0% {} 100% {} }', | ||
}, | ||
{ | ||
code: '@keyframes foo { from {} to {} }', | ||
}, | ||
], | ||
|
||
reject: [ | ||
{ | ||
code: '@keyframes foo { from {} from {} }', | ||
message: messages.rejected('from'), | ||
line: 1, | ||
column: 26, | ||
endLine: 1, | ||
endColumn: 30, | ||
}, | ||
{ | ||
code: '@keyframes foo { 0% {} 0% {} }', | ||
message: messages.rejected('0%'), | ||
line: 1, | ||
column: 24, | ||
endLine: 1, | ||
endColumn: 26, | ||
}, | ||
{ | ||
code: '@keyframes foo { from {} FROM {} }', | ||
message: messages.rejected('FROM'), | ||
}, | ||
{ | ||
code: '@keyframes foo { from {} to {} to {} }', | ||
message: messages.rejected('to'), | ||
}, | ||
{ | ||
code: '@keyframes foo { 0% {} 0% {} 100% {} }', | ||
message: messages.rejected('0%'), | ||
}, | ||
{ | ||
code: '@-webkit-keyframes foo { 0% {} 0% {} 100% {} }', | ||
message: messages.rejected('0%'), | ||
}, | ||
{ | ||
code: '@-moz-keyframes foo { 0% {} 0% {} 100% {} }', | ||
message: messages.rejected('0%'), | ||
}, | ||
{ | ||
code: '@keyframes foo { 0% {} 0%, 100% {} }', | ||
message: messages.rejected('0%'), | ||
}, | ||
], | ||
}); | ||
|
||
testRule({ | ||
ruleName, | ||
config: [true], | ||
customSyntax: 'postcss-scss', | ||
|
||
accept: [ | ||
{ | ||
code: '@keyframes foo { #{$bar} {} #{$bar} {} }', | ||
description: 'SCSS interpolation in selector', | ||
}, | ||
], | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
'use strict'; | ||
|
||
const isStandardSyntaxSelector = require('../../utils/isStandardSyntaxSelector'); | ||
const report = require('../../utils/report'); | ||
const ruleMessages = require('../../utils/ruleMessages'); | ||
const validateOptions = require('../../utils/validateOptions'); | ||
|
||
const ruleName = 'keyframe-block-no-duplicate-selectors'; | ||
|
||
const messages = ruleMessages(ruleName, { | ||
rejected: (selector) => `Unexpected duplicate "${selector}"`, | ||
}); | ||
|
||
const meta = { | ||
url: 'https://stylelint.io/user-guide/rules/list/keyframe-block-no-duplicate-selectors', | ||
}; | ||
|
||
/** @type {import('stylelint').Rule} */ | ||
const rule = (primary) => { | ||
return (root, result) => { | ||
const validOptions = validateOptions(result, ruleName, { actual: primary }); | ||
|
||
if (!validOptions) { | ||
return; | ||
} | ||
|
||
root.walkAtRules(/^(-(moz|webkit)-)?keyframes$/i, (atRuleKeyframes) => { | ||
const selectors = new Set(); | ||
|
||
atRuleKeyframes.walkRules((keyframeRule) => { | ||
const ruleSelectors = keyframeRule.selectors; | ||
|
||
ruleSelectors.forEach((selector) => { | ||
if (!isStandardSyntaxSelector(selector)) { | ||
return; | ||
} | ||
|
||
const normalizedSelector = selector.toLowerCase(); | ||
|
||
const isDuplicate = selectors.has(normalizedSelector); | ||
|
||
if (isDuplicate) { | ||
report({ | ||
message: messages.rejected(selector), | ||
node: keyframeRule, | ||
result, | ||
ruleName, | ||
word: selector, | ||
}); | ||
|
||
return; | ||
} | ||
|
||
selectors.add(normalizedSelector); | ||
}); | ||
}); | ||
}); | ||
}; | ||
}; | ||
|
||
rule.ruleName = ruleName; | ||
rule.messages = messages; | ||
rule.meta = meta; | ||
module.exports = rule; |