stylelint-gamut 1.3.1 → 1.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +19 -14
- package/src/README.md +3 -7
- package/src/index.js +100 -79
- package/src/utils.js +69 -62
package/package.json
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stylelint-gamut",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.3",
|
|
4
4
|
"description": "Stylelint plugin for working with different color spaces",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"scripts": {
|
|
7
|
+
"format": "prettier --write",
|
|
8
|
+
"lint": "eslint --fix",
|
|
7
9
|
"test": "jest",
|
|
8
10
|
"prepare": "husky install"
|
|
9
11
|
},
|
|
10
12
|
"lint-staged": {
|
|
11
|
-
"*.js": "
|
|
13
|
+
"*.js": "npm run lint",
|
|
14
|
+
".": "npm run format"
|
|
12
15
|
},
|
|
13
16
|
"engines": {
|
|
14
17
|
"node": ">=16.0.0"
|
|
@@ -40,19 +43,21 @@
|
|
|
40
43
|
"stylelint": "^14.0.0 || ^15.0.0"
|
|
41
44
|
},
|
|
42
45
|
"devDependencies": {
|
|
43
|
-
"eslint": "^8.
|
|
44
|
-
"eslint-config-
|
|
45
|
-
"eslint-
|
|
46
|
-
"eslint-plugin-
|
|
47
|
-
"eslint-plugin-
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"jest
|
|
51
|
-
"jest-
|
|
52
|
-
"
|
|
53
|
-
"
|
|
46
|
+
"eslint": "^8.43.0",
|
|
47
|
+
"eslint-config-prettier": "^8.8.0",
|
|
48
|
+
"eslint-config-standard": "^17.1.0",
|
|
49
|
+
"eslint-plugin-import": "^2.27.5",
|
|
50
|
+
"eslint-plugin-n": "^16.0.0",
|
|
51
|
+
"eslint-plugin-promise": "^6.1.1",
|
|
52
|
+
"husky": "^8.0.3",
|
|
53
|
+
"jest": "^29.5.0",
|
|
54
|
+
"jest-cli": "^29.5.0",
|
|
55
|
+
"jest-preset-stylelint": "^6.1.0",
|
|
56
|
+
"lint-staged": "^13.2.2",
|
|
57
|
+
"prettier": "^2.8.8",
|
|
58
|
+
"stylelint": "^15.7.0"
|
|
54
59
|
},
|
|
55
60
|
"dependencies": {
|
|
56
|
-
"colorjs.io": "^0.4.
|
|
61
|
+
"colorjs.io": "^0.4.5"
|
|
57
62
|
}
|
|
58
63
|
}
|
package/src/README.md
CHANGED
|
@@ -4,9 +4,7 @@ Disallow out of gamut range colors.
|
|
|
4
4
|
|
|
5
5
|
<!-- prettier-ignore -->
|
|
6
6
|
```css
|
|
7
|
-
a { color: lch(48% 82 283) }
|
|
8
|
-
/** ↑
|
|
9
|
-
* This color is out of sRGB gamut range */
|
|
7
|
+
a { color: lch(48% 82 283) } /* This color is out of sRGB gamut range */
|
|
10
8
|
```
|
|
11
9
|
|
|
12
10
|
Colors declared using lch, oklch, lab and oklab functions can move from sRGB to P3 or Rec2020 color spaces, which are not supported by most of screens right now. It can happen by mistake. For instance, by converting colors from one space to another.
|
|
@@ -17,20 +15,18 @@ This rule checks if the color is in sRGB, p3 or rec2020 space. If it's not in sR
|
|
|
17
15
|
@media (color-gamut: p3) {
|
|
18
16
|
a {
|
|
19
17
|
color: lch(29.8% 42.5 109.485);
|
|
18
|
+
/* This color is in p3 gamut range */
|
|
20
19
|
}
|
|
21
20
|
}
|
|
22
|
-
/** ↑
|
|
23
|
-
* This color is in p3 gamut range */
|
|
24
21
|
```
|
|
25
22
|
|
|
26
23
|
```css
|
|
27
24
|
@media (color-gamut: rec2020) {
|
|
28
25
|
a {
|
|
29
26
|
color: lch(25.1% 42.5 109.485);
|
|
27
|
+
/* This color is in rec2020 gamut range */
|
|
30
28
|
}
|
|
31
29
|
}
|
|
32
|
-
/** ↑
|
|
33
|
-
* This color is in rec2020 gamut range */
|
|
34
30
|
```
|
|
35
31
|
|
|
36
32
|
In case of **css custom properties** you should wrap either the custom property declaration:
|
package/src/index.js
CHANGED
|
@@ -1,87 +1,108 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
2
|
|
|
3
|
-
const stylelint = require(
|
|
4
|
-
const Color = require(
|
|
5
|
-
const {
|
|
3
|
+
const stylelint = require("stylelint");
|
|
4
|
+
const Color = require("colorjs.io").default;
|
|
5
|
+
const {
|
|
6
|
+
isInColorGamutP3MediaQuery,
|
|
7
|
+
isStandardSyntaxProperty,
|
|
8
|
+
declarationValueIndex,
|
|
9
|
+
isInColorGamutRec2020MediaQuery,
|
|
10
|
+
startsWithNumber,
|
|
11
|
+
} = require("./utils");
|
|
6
12
|
|
|
7
|
-
const ruleName =
|
|
13
|
+
const ruleName = "gamut/color-no-out-gamut-range";
|
|
8
14
|
|
|
9
15
|
const messages = stylelint.utils.ruleMessages(ruleName, {
|
|
10
|
-
|
|
11
|
-
})
|
|
16
|
+
rejected: color => `Unexpected color out of gamut range "${color}"`,
|
|
17
|
+
});
|
|
12
18
|
|
|
13
19
|
const meta = {
|
|
14
|
-
|
|
15
|
-
}
|
|
20
|
+
url: "https://github.com/fpetrakov/stylelint-gamut/blob/master/src/README.md",
|
|
21
|
+
};
|
|
16
22
|
|
|
17
23
|
/** @type {import('stylelint').Rule} */
|
|
18
|
-
const ruleFunction =
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
24
|
+
const ruleFunction = primary => {
|
|
25
|
+
return (root, result) => {
|
|
26
|
+
const validOptions = stylelint.utils.validateOptions(result, ruleName, {
|
|
27
|
+
actual: primary,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
if (!validOptions) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const customProperties = {};
|
|
35
|
+
|
|
36
|
+
root.walkDecls(decl => {
|
|
37
|
+
if (!isStandardSyntaxProperty(decl.prop)) return;
|
|
38
|
+
|
|
39
|
+
const values = decl.value.match(
|
|
40
|
+
/(oklch|oklab|lab|lch|var)\([^)]+\)/g,
|
|
41
|
+
);
|
|
42
|
+
if (values) {
|
|
43
|
+
for (const value of values) {
|
|
44
|
+
check(value);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// value is e.g. lch(48% 82 283 / 67%)
|
|
49
|
+
function check(value) {
|
|
50
|
+
const shouldBeIgnored =
|
|
51
|
+
!value.startsWith("var(--") &&
|
|
52
|
+
(value.includes("var(--") ||
|
|
53
|
+
!startsWithNumber(value.split("(")[1]));
|
|
54
|
+
|
|
55
|
+
if (shouldBeIgnored) return;
|
|
56
|
+
|
|
57
|
+
let customPropValue;
|
|
58
|
+
if (value.startsWith("var(--")) {
|
|
59
|
+
const varName = value.slice(4, -1);
|
|
60
|
+
if (
|
|
61
|
+
customProperties[varName] &&
|
|
62
|
+
!customProperties[varName].inQuery
|
|
63
|
+
) {
|
|
64
|
+
customPropValue = customProperties[varName].value;
|
|
65
|
+
} else {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const color = new Color(customPropValue || value);
|
|
71
|
+
|
|
72
|
+
if (color.inGamut("srgb")) return;
|
|
73
|
+
if (color.inGamut("p3") && isInColorGamutP3MediaQuery(decl))
|
|
74
|
+
return;
|
|
75
|
+
if (
|
|
76
|
+
color.inGamut("rec2020") &&
|
|
77
|
+
isInColorGamutRec2020MediaQuery(decl)
|
|
78
|
+
)
|
|
79
|
+
return;
|
|
80
|
+
|
|
81
|
+
if (decl.prop && decl.prop.startsWith("--")) {
|
|
82
|
+
customProperties[decl.prop] = {
|
|
83
|
+
value: decl.value.trim(),
|
|
84
|
+
inQuery: false,
|
|
85
|
+
};
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const index = declarationValueIndex(decl);
|
|
90
|
+
const endIndex = index + decl.value.length;
|
|
91
|
+
stylelint.utils.report({
|
|
92
|
+
message: messages.rejected(value),
|
|
93
|
+
node: decl,
|
|
94
|
+
index,
|
|
95
|
+
endIndex,
|
|
96
|
+
result,
|
|
97
|
+
ruleName,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
ruleFunction.ruleName = ruleName;
|
|
105
|
+
ruleFunction.messages = messages;
|
|
106
|
+
ruleFunction.meta = meta;
|
|
107
|
+
|
|
108
|
+
module.exports = stylelint.createPlugin(ruleName, ruleFunction);
|
package/src/utils.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Get the index of a declaration's value
|
|
@@ -6,26 +6,22 @@
|
|
|
6
6
|
* @param {import('postcss').Declaration} decl
|
|
7
7
|
* @returns {number}
|
|
8
8
|
*/
|
|
9
|
-
function declarationValueIndex
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return count
|
|
28
|
-
}, 0)
|
|
9
|
+
function declarationValueIndex(decl) {
|
|
10
|
+
const raws = decl.raws;
|
|
11
|
+
|
|
12
|
+
return [
|
|
13
|
+
raws.prop && raws.prop.prefix,
|
|
14
|
+
(raws.prop && raws.prop.raw) || decl.prop,
|
|
15
|
+
raws.prop && raws.prop.suffix,
|
|
16
|
+
raws.between || ":",
|
|
17
|
+
raws.value && raws.value.prefix,
|
|
18
|
+
].reduce((count, str) => {
|
|
19
|
+
if (str) {
|
|
20
|
+
return count + str.length;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return count;
|
|
24
|
+
}, 0);
|
|
29
25
|
}
|
|
30
26
|
|
|
31
27
|
/**
|
|
@@ -34,78 +30,89 @@ function declarationValueIndex (decl) {
|
|
|
34
30
|
* @param {string} property
|
|
35
31
|
* @return {boolean} If `true`, the property is standard
|
|
36
32
|
*/
|
|
37
|
-
function isStandardSyntaxProperty
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
33
|
+
function isStandardSyntaxProperty(property) {
|
|
34
|
+
// SCSS var (e.g. $var: x), list (e.g. $list: (x)) or map (e.g. $map: (key:value))
|
|
35
|
+
if (property.startsWith("$")) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Less var (e.g. @var: x)
|
|
40
|
+
if (property.startsWith("@")) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// SCSS or Less interpolation
|
|
45
|
+
if (/#{.+?}|@{.+?}|\$\(.+?\)/.test(property)) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return true;
|
|
54
50
|
}
|
|
55
51
|
|
|
56
52
|
/**
|
|
57
53
|
* @param {import('postcss').Declaration} decl
|
|
58
54
|
* @returns {boolean}
|
|
59
55
|
*/
|
|
60
|
-
function isInColorGamutP3MediaQuery
|
|
61
|
-
|
|
56
|
+
function isInColorGamutP3MediaQuery(decl) {
|
|
57
|
+
const parentMediaQuery = getMediaQueryParent(decl);
|
|
62
58
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
59
|
+
if (
|
|
60
|
+
parentMediaQuery &&
|
|
61
|
+
/\(color-gamut:\s*p3\)/.test(parentMediaQuery.params)
|
|
62
|
+
) {
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
66
65
|
|
|
67
|
-
|
|
66
|
+
return false;
|
|
68
67
|
}
|
|
69
68
|
|
|
70
69
|
/**
|
|
71
70
|
* @param {import('postcss').Declaration} decl
|
|
72
71
|
* @returns {boolean}
|
|
73
72
|
*/
|
|
74
|
-
function isInColorGamutRec2020MediaQuery
|
|
75
|
-
|
|
73
|
+
function isInColorGamutRec2020MediaQuery(decl) {
|
|
74
|
+
const parentMediaQuery = getMediaQueryParent(decl);
|
|
76
75
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
76
|
+
if (
|
|
77
|
+
parentMediaQuery &&
|
|
78
|
+
/\(color-gamut:\s*rec2020\)/.test(parentMediaQuery.params)
|
|
79
|
+
) {
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
80
82
|
|
|
81
|
-
|
|
83
|
+
return false;
|
|
82
84
|
}
|
|
83
85
|
|
|
84
86
|
/**
|
|
85
87
|
* @param {import('postcss').Declaration} decl
|
|
86
88
|
* @returns {boolean | decl}
|
|
87
89
|
*/
|
|
88
|
-
function getMediaQueryParent
|
|
89
|
-
|
|
90
|
+
function getMediaQueryParent(decl) {
|
|
91
|
+
let parent = decl.parent;
|
|
90
92
|
|
|
91
|
-
|
|
93
|
+
if (parent && isMediaQuery(parent)) return parent;
|
|
92
94
|
|
|
93
|
-
|
|
95
|
+
parent = parent.parent;
|
|
94
96
|
|
|
95
|
-
|
|
97
|
+
return isMediaQuery(parent) && parent;
|
|
96
98
|
}
|
|
97
99
|
|
|
98
100
|
/**
|
|
99
101
|
* @param {import('postcss').Declaration} decl
|
|
100
102
|
* @returns {boolean}
|
|
101
103
|
*/
|
|
102
|
-
function isMediaQuery
|
|
103
|
-
|
|
104
|
+
function isMediaQuery(decl) {
|
|
105
|
+
return decl.type === "atrule" && decl.name === "media";
|
|
104
106
|
}
|
|
105
107
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
declarationValueIndex,
|
|
109
|
-
isInColorGamutP3MediaQuery,
|
|
110
|
-
isInColorGamutRec2020MediaQuery
|
|
108
|
+
function startsWithNumber(str) {
|
|
109
|
+
return /^\d/.test(str);
|
|
111
110
|
}
|
|
111
|
+
|
|
112
|
+
module.exports = {
|
|
113
|
+
isStandardSyntaxProperty,
|
|
114
|
+
declarationValueIndex,
|
|
115
|
+
isInColorGamutP3MediaQuery,
|
|
116
|
+
isInColorGamutRec2020MediaQuery,
|
|
117
|
+
startsWithNumber,
|
|
118
|
+
};
|