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 keyframe-selector-notation #6164

Merged
merged 7 commits into from Jun 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -134,6 +134,10 @@ Within each cateogory, the rules are grouped by the [_thing_](http://apps.workfl

- [`import-notation`](../../../lib/rules/import-notation/README.md): Specify string or URL notation for `@import` rules (Autofixable).

### Keyframe selector

- [`keyframe-selector-notation`](../../../lib/rules/keyframe-selector-notation/README.md): Specify keyword or percentage notation for keyframe selectors.

### Keyframes

- [`keyframes-name-pattern`](../../../lib/rules/keyframes-name-pattern/README.md): Specify a pattern for keyframe names.
Expand Down
1 change: 1 addition & 0 deletions lib/rules/index.js
Expand Up @@ -122,6 +122,7 @@ const rules = {
'import-notation': importLazy('./import-notation'),
'keyframe-block-no-duplicate-selectors': importLazy('./keyframe-block-no-duplicate-selectors'),
'keyframe-declaration-no-important': importLazy('./keyframe-declaration-no-important'),
'keyframe-selector-notation': importLazy('./keyframe-selector-notation'),
'keyframes-name-pattern': importLazy('./keyframes-name-pattern'),
'length-zero-no-unit': importLazy('./length-zero-no-unit'),
linebreaks: importLazy('./linebreaks'),
Expand Down
54 changes: 54 additions & 0 deletions lib/rules/keyframe-selector-notation/README.md
@@ -0,0 +1,54 @@
# keyframe-selector-notation

Specify keyword or percentage notation for keyframe selectors.

<!-- prettier-ignore -->
```css
@keyframes foo { from {} to {} }
/** ↑ ↑
* These notations */
```

The keyword `from` is equivalent to the value `0%`. The keyword `to` is equivalent to the value `100%`.

The [`fix` option](../../../docs/user-guide/usage/options.md#fix) can automatically fix all of the problems reported by this rule.

## Options

`string`: `"keyword"|"percentage"`

### `"keyword"`

Keyframe selectors _must always_ use the keyword notation.

The following pattern is considered a problem:

<!-- prettier-ignore -->
```css
@keyframes foo { 0% {} 100% {} }
```

The following pattern is _not_ considered a problem:

<!-- prettier-ignore -->
```css
@keyframes foo { from {} to {} }
```

### `"percentage"`

Keyframe selectors _must always_ use the percentage notation.

The following pattern is considered a problem:

<!-- prettier-ignore -->
```css
@keyframes foo { from {} to {} }
```

The following pattern is _not_ considered a problem:

<!-- prettier-ignore -->
```css
@keyframes foo { 0% {} 100% {} }
```
281 changes: 281 additions & 0 deletions lib/rules/keyframe-selector-notation/__tests__/index.js
@@ -0,0 +1,281 @@
'use strict';

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

testRule({
ruleName,
config: ['keyword'],
fix: true,

accept: [
{
code: '@keyframes foo { from {} }',
},
{
code: '@keyframes foo { to {} }',
},
{
code: '@keyframes foo { from {} to {} }',
},
{
code: '@keyframes foo { from {} from {} to {} }',
description: 'duplicate selectors',
},
{
code: '@keyframes foo { from {} 50% {} to {} }',
description: 'mixed notation',
},
{
code: '@keyframes foo { from, to {} }',
description: 'selector lists',
},
],

reject: [
{
code: '@keyframes foo { 0% {} }',
fixed: '@keyframes foo { from {} }',
message: messages.expected('0%', 'from'),
line: 1,
column: 18,
endLine: 1,
endColumn: 20,
},
{
code: '@keyframes foo { 100% {} }',
fixed: '@keyframes foo { to {} }',
message: messages.expected('100%', 'to'),
line: 1,
column: 18,
endLine: 1,
endColumn: 22,
},
{
code: '@keyframes foo { 0% {} 100% {} }',
fixed: '@keyframes foo { from {} to {} }',
warnings: [
{
message: messages.expected('0%', 'from'),
line: 1,
column: 18,
endLine: 1,
endColumn: 20,
},
{
message: messages.expected('100%', 'to'),
line: 1,
column: 24,
endLine: 1,
endColumn: 28,
},
],
},
{
code: '@keyframes foo { 0% {} 0% {} 100% {} }',
fixed: '@keyframes foo { from {} from {} to {} }',
description: 'duplicate selectors',
warnings: [
{
message: messages.expected('0%', 'from'),
line: 1,
column: 18,
endLine: 1,
endColumn: 20,
},
{
message: messages.expected('0%', 'from'),
line: 1,
column: 24,
endLine: 1,
endColumn: 26,
},
{
message: messages.expected('100%', 'to'),
line: 1,
column: 30,
endLine: 1,
endColumn: 34,
},
],
},
{
code: '@keyframes foo { 0% {} 50% {} 100% {} }',
fixed: '@keyframes foo { from {} 50% {} to {} }',
description: 'mixed notation - note that the middle is untouched',
warnings: [
{
message: messages.expected('0%', 'from'),
line: 1,
column: 18,
endLine: 1,
endColumn: 20,
},
{
message: messages.expected('100%', 'to'),
line: 1,
column: 31,
endLine: 1,
endColumn: 35,
},
],
},
{
code: '@keyframes foo { 0%, 100% {} }',
fixed: '@keyframes foo { from, to {} }',
description: 'selector lists',
warnings: [
{
message: messages.expected('0%', 'from'),
line: 1,
column: 18,
endLine: 1,
endColumn: 20,
},
{
message: messages.expected('100%', 'to'),
line: 1,
column: 22,
endLine: 1,
endColumn: 26,
},
],
},
],
});

testRule({
ruleName,
config: ['percentage'],
fix: true,

accept: [
{
code: '@keyframes foo { 0% {} }',
},
{
code: '@keyframes foo { 100% {} }',
},
{
code: '@keyframes foo { 0% {} 100% {} }',
},
{
code: '@keyframes foo { 0% {} 0% {} 100% {} }',
description: 'duplicate selectors',
},
{
code: '@keyframes foo { 0%, 100% {} }',
description: 'selector lists',
},
],

reject: [
{
code: '@keyframes foo { from {} }',
fixed: '@keyframes foo { 0% {} }',
message: messages.expected('from', '0%'),
line: 1,
column: 18,
endLine: 1,
endColumn: 22,
},
{
code: '@keyframes foo { to {} }',
fixed: '@keyframes foo { 100% {} }',
message: messages.expected('to', '100%'),
line: 1,
column: 18,
endLine: 1,
endColumn: 20,
},
{
code: '@keyframes foo { from {} to {} }',
fixed: '@keyframes foo { 0% {} 100% {} }',
warnings: [
{
message: messages.expected('from', '0%'),
line: 1,
column: 18,
endLine: 1,
endColumn: 22,
},
{
message: messages.expected('to', '100%'),
line: 1,
column: 26,
endLine: 1,
endColumn: 28,
},
],
},
{
code: '@keyframes foo { from {} from {} to {} }',
fixed: '@keyframes foo { 0% {} 0% {} 100% {} }',
description: 'duplicate selectors',
warnings: [
{
message: messages.expected('from', '0%'),
line: 1,
column: 18,
endLine: 1,
endColumn: 22,
},
{
message: messages.expected('from', '0%'),
line: 1,
column: 26,
endLine: 1,
endColumn: 30,
},
{
message: messages.expected('to', '100%'),
line: 1,
column: 34,
endLine: 1,
endColumn: 36,
},
],
},
{
code: '@keyframes foo { from {} 50% {} to {} }',
fixed: '@keyframes foo { 0% {} 50% {} 100% {} }',
description: 'mixed notation - note that the middle is untouched',
warnings: [
{
message: messages.expected('from', '0%'),
line: 1,
column: 18,
endLine: 1,
endColumn: 22,
},
{
message: messages.expected('to', '100%'),
line: 1,
column: 33,
endLine: 1,
endColumn: 35,
},
],
},
{
code: '@keyframes foo { from, to {} }',
fixed: '@keyframes foo { 0%, 100% {} }',
description: 'selector lists',
warnings: [
{
message: messages.expected('from', '0%'),
line: 1,
column: 18,
endLine: 1,
endColumn: 22,
},
{
message: messages.expected('to', '100%'),
line: 1,
column: 24,
endLine: 1,
endColumn: 26,
},
],
},
],
});