Skip to content

Commit

Permalink
Add color-hex-alpha rule (#5316)
Browse files Browse the repository at this point in the history
  • Loading branch information
jeddy3 committed May 29, 2021
1 parent 0f6da86 commit ea107d7
Show file tree
Hide file tree
Showing 5 changed files with 228 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/user-guide/rules/list.md
Expand Up @@ -93,6 +93,7 @@ Grouped first by the following categories and then by the [_thing_](http://apps.
### Color

- [`color-function-notation`](../../../lib/rules/color-function-notation/README.md): Specify modern or legacy notation for applicable color-functions (Autofixable).
- [`color-hex-alpha`](../../../lib/rules/color-hex-alpha/README.md): Require or disallow alpha channel for hex colors.
- [`color-named`](../../../lib/rules/color-named/README.md): Require (where possible) or disallow named colors.
- [`color-no-hex`](../../../lib/rules/color-no-hex/README.md): Disallow hex colors.

Expand Down
66 changes: 66 additions & 0 deletions lib/rules/color-hex-alpha/README.md
@@ -0,0 +1,66 @@
# color-hex-alpha

Require or disallow alpha channel for hex colors.

<!-- prettier-ignore -->
```css
a { color: #fffa }
/** ↑
* This alpha channel */
```

## Options

`string`: `"always"|"never"`

### `"always"`

The following patterns are considered violations:

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

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

The following patterns are _not_ considered violations:

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

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

### `"never"`

The following patterns are considered violations:

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

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

The following patterns are _not_ considered violations:

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

<!-- prettier-ignore -->
```css
a { color: #ffffff; }
```
92 changes: 92 additions & 0 deletions lib/rules/color-hex-alpha/__tests__/index.js
@@ -0,0 +1,92 @@
'use strict';

const postcssScss = require('postcss-scss');

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

testRule({
ruleName,
config: 'always',
accept: [
{
code: 'a { color: #ffff; }',
},
{
code: 'a { color: #ffffffff; }',
},
{
code: 'a { background: linear-gradient(to left, #fffa, #000000aa 100%); }',
},
{
code: 'a { background: url(#fff); }',
},
],
reject: [
{
code: 'a { color: #fff; }',
message: messages.expected('#fff'),
line: 1,
column: 12,
},
{
code: 'a { color: #ffffff; }',
message: messages.expected('#ffffff'),
line: 1,
column: 12,
},
{
code: 'a { background: linear-gradient(to left, #fff, #000000 100%); }',
warnings: [
{ message: messages.expected('#fff'), line: 1, column: 42 },
{ message: messages.expected('#000000'), line: 1, column: 48 },
],
},
],
});

testRule({
ruleName,
config: 'never',
accept: [
{
code: 'a { color: #fff; }',
},
{
code: 'a { color: #ffffff; }',
},
],
reject: [
{
code: 'a { color: #ffff; }',
message: messages.unexpected('#ffff'),
line: 1,
column: 12,
},
],
});

testRule({
ruleName,
config: 'always',
customSyntax: postcssScss,

accept: [
{
code: 'a { color: #{f}; }',
description: 'scss interpolation of 3 characters',
},
{
code: '$var: #ffff;',
description: 'alpha channel scss variable',
},
],
reject: [
{
code: '$var: #fff',
message: messages.expected('#fff'),
line: 1,
column: 7,
description: 'no alpha channel scss variable',
},
],
});
68 changes: 68 additions & 0 deletions lib/rules/color-hex-alpha/index.js
@@ -0,0 +1,68 @@
// @ts-nocheck
'use strict';

const declarationValueIndex = require('../../utils/declarationValueIndex');
const report = require('../../utils/report');
const ruleMessages = require('../../utils/ruleMessages');
const validateOptions = require('../../utils/validateOptions');
const valueParser = require('postcss-value-parser');

const ruleName = 'color-hex-alpha';

const messages = ruleMessages(ruleName, {
expected: (hex) => `Expected alpha channel in "${hex}"`,
unexpected: (hex) => `Unexpected alpha channel in "${hex}"`,
});

const HEX = /^#([\da-f]{3,4}|[\da-f]{6}|[\da-f]{8})$/i;

function rule(primary) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: primary,
possible: ['always', 'never'],
});

if (!validOptions) return;

root.walkDecls((decl) => {
const parsedValue = valueParser(decl.value);

parsedValue.walk((node) => {
if (isUrlFunction(node)) return false;

if (!isHexColor(node)) return;

const { value } = node;

if (primary === 'always' && hasAlphaChannel(value)) return;

if (primary === 'never' && !hasAlphaChannel(value)) return;

report({
message: primary === 'never' ? messages.unexpected(value) : messages.expected(value),
node: decl,
index: declarationValueIndex(decl) + node.sourceIndex,
result,
ruleName,
});
});
});
};
}

function isUrlFunction({ type, value }) {
return type === 'function' && value === 'url';
}

function isHexColor({ type, value }) {
return type === 'word' && HEX.test(value);
}

function hasAlphaChannel(hex) {
return hex.length === 5 || hex.length === 9;
}

rule.ruleName = ruleName;
rule.messages = messages;
module.exports = rule;
1 change: 1 addition & 0 deletions lib/rules/index.js
Expand Up @@ -51,6 +51,7 @@ const rules = {
require('./block-opening-brace-space-before'),
)(),
'color-function-notation': importLazy(() => require('./color-function-notation'))(),
'color-hex-alpha': importLazy(() => require('./color-hex-alpha'))(),
'color-hex-case': importLazy(() => require('./color-hex-case'))(),
'color-hex-length': importLazy(() => require('./color-hex-length'))(),
'color-named': importLazy(() => require('./color-named'))(),
Expand Down

0 comments on commit ea107d7

Please sign in to comment.