stylelint-gamut 1.0.2 → 1.1.0
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/README.md +1 -0
- package/package.json +1 -1
- package/src/index.js +21 -30
- package/src/index.test.js +15 -2
- package/src/utils.js +127 -0
- package/src/utils/declarationValueIndex.js +0 -29
- package/src/utils/getDeclarationValue.js +0 -11
- package/src/utils/isAtRule.js +0 -7
- package/src/utils/isCustomProperty.js +0 -10
- package/src/utils/isStandardSyntaxProperty.js +0 -25
package/README.md
CHANGED
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -3,11 +3,15 @@
|
|
|
3
3
|
const valueParser = require('postcss-value-parser')
|
|
4
4
|
const stylelint = require('stylelint')
|
|
5
5
|
const Color = require('colorjs.io').default
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
|
|
7
|
+
const {
|
|
8
|
+
isStandardSyntaxProperty,
|
|
9
|
+
isCustomProperty,
|
|
10
|
+
getDeclarationValue,
|
|
11
|
+
declarationValueIndex,
|
|
12
|
+
isInColorGamutP3MediaQuery,
|
|
13
|
+
isInColorGamutRec2020MediaQuery
|
|
14
|
+
} = require('./utils')
|
|
11
15
|
|
|
12
16
|
const ruleName = 'gamut/color-no-out-gamut-range'
|
|
13
17
|
|
|
@@ -19,6 +23,8 @@ const meta = {
|
|
|
19
23
|
url: 'https://stylelint.io/user-guide/rules/list/color-no-out-gamut-range'
|
|
20
24
|
}
|
|
21
25
|
|
|
26
|
+
const COLOR_FUNCTIONS = new Set(['lch', 'lab', 'oklch', 'oklab'])
|
|
27
|
+
|
|
22
28
|
/** @type {import('stylelint').Rule} */
|
|
23
29
|
const ruleFunction = (primary) => {
|
|
24
30
|
return (root, result) => {
|
|
@@ -40,22 +46,21 @@ const ruleFunction = (primary) => {
|
|
|
40
46
|
parsedValue.walk((node, _index, nodes) => {
|
|
41
47
|
if (node.type !== 'function') return
|
|
42
48
|
|
|
43
|
-
if (
|
|
44
|
-
node.value !== 'lch' &&
|
|
45
|
-
node.value !== 'lab' &&
|
|
46
|
-
node.value !== 'oklch' &&
|
|
47
|
-
node.value !== 'oklab'
|
|
48
|
-
) {
|
|
49
|
+
if (!COLOR_FUNCTIONS.has(node.value)) {
|
|
49
50
|
return
|
|
50
51
|
}
|
|
51
52
|
|
|
52
|
-
const
|
|
53
|
-
'srgb'
|
|
54
|
-
)
|
|
53
|
+
const color = new Color(valueParser.stringify(nodes))
|
|
55
54
|
|
|
56
|
-
|
|
55
|
+
const isInSrgbGamut = color.inGamut('srgb')
|
|
56
|
+
const isInP3Query = isInColorGamutP3MediaQuery(decl)
|
|
57
|
+
if (isInSrgbGamut || isInP3Query) return
|
|
57
58
|
|
|
58
|
-
|
|
59
|
+
const isInP3Gamut = color.inGamut('p3')
|
|
60
|
+
if (isInP3Gamut && isInP3Query) return
|
|
61
|
+
|
|
62
|
+
const isInRec2020Gamut = color.inGamut('rec2020')
|
|
63
|
+
if (isInRec2020Gamut && isInColorGamutRec2020MediaQuery(decl)) return
|
|
59
64
|
|
|
60
65
|
const index = declarationValueIndex(decl) + node.sourceIndex
|
|
61
66
|
const endIndex = index + decl.value.length
|
|
@@ -73,20 +78,6 @@ const ruleFunction = (primary) => {
|
|
|
73
78
|
}
|
|
74
79
|
}
|
|
75
80
|
|
|
76
|
-
/**
|
|
77
|
-
* @param {import('postcss').Declaration} decl
|
|
78
|
-
* @returns {boolean}
|
|
79
|
-
*/
|
|
80
|
-
function isInColorGamutP3MediaQuery (decl) {
|
|
81
|
-
if (decl.parent && decl.parent.parent && isAtRule(decl.parent.parent)) {
|
|
82
|
-
const parent = decl.parent.parent
|
|
83
|
-
|
|
84
|
-
return parent.name === 'media' && parent.params === '(color-gamut: p3)'
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return false
|
|
88
|
-
}
|
|
89
|
-
|
|
90
81
|
ruleFunction.ruleName = ruleName
|
|
91
82
|
ruleFunction.messages = messages
|
|
92
83
|
ruleFunction.meta = meta
|
package/src/index.test.js
CHANGED
|
@@ -9,12 +9,16 @@ testRule({
|
|
|
9
9
|
accept: [
|
|
10
10
|
{
|
|
11
11
|
code: '@media (color-gamut: p3) { a { color: lch(48% 82 283); } }',
|
|
12
|
-
description: 'lch out of srgb gamut but wrapped in media query'
|
|
12
|
+
description: 'lch out of srgb gamut but wrapped in p3 media query'
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
code: '@media (color-gamut: rec2020) { a { color: lch(48% 82 283); } }',
|
|
16
|
+
description: 'lch out of p3 gamut but wrapped in rec2020 media query'
|
|
13
17
|
},
|
|
14
18
|
{
|
|
15
19
|
code: '@media (color-gamut: p3) { a { color: oklch(48% 82 283 / 73%); } }',
|
|
16
20
|
description:
|
|
17
|
-
'oklch with alpha out of srgb gamut but wrapped in media query'
|
|
21
|
+
'oklch with alpha out of srgb gamut but wrapped in p3 media query'
|
|
18
22
|
},
|
|
19
23
|
{
|
|
20
24
|
code: 'a { color: lch(50% 0 0); }',
|
|
@@ -48,6 +52,15 @@ testRule({
|
|
|
48
52
|
endLine: 1,
|
|
49
53
|
endColumn: 27
|
|
50
54
|
},
|
|
55
|
+
{
|
|
56
|
+
code: '@media (color-gamut: srgb) { a { color: lch(48% 82 283); } }',
|
|
57
|
+
description: 'lch is out of srgb and wrapped in srgb media query',
|
|
58
|
+
message: messages.rejected('lch(48% 82 283)'),
|
|
59
|
+
line: 1,
|
|
60
|
+
column: 41,
|
|
61
|
+
endLine: 1,
|
|
62
|
+
endColumn: 56
|
|
63
|
+
},
|
|
51
64
|
{
|
|
52
65
|
code: 'a { color: lch(48% 82 283 / 72%); }',
|
|
53
66
|
description: 'lch with alpha',
|
package/src/utils.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Get the index of a declaration's value
|
|
5
|
+
*
|
|
6
|
+
* @param {import('postcss').Declaration} decl
|
|
7
|
+
* @returns {number}
|
|
8
|
+
*/
|
|
9
|
+
function declarationValueIndex (decl) {
|
|
10
|
+
const raws = decl.raws
|
|
11
|
+
|
|
12
|
+
return [
|
|
13
|
+
// @ts-expect-error -- TS2571: Object is of type 'unknown'.
|
|
14
|
+
raws.prop && raws.prop.prefix,
|
|
15
|
+
// @ts-expect-error -- TS2571: Object is of type 'unknown'.
|
|
16
|
+
(raws.prop && raws.prop.raw) || decl.prop,
|
|
17
|
+
// @ts-expect-error -- TS2571: Object is of type 'unknown'.
|
|
18
|
+
raws.prop && raws.prop.suffix,
|
|
19
|
+
raws.between || ':',
|
|
20
|
+
// @ts-expect-error -- TS2339: Property 'prefix' does not exist on type '{ value: string; raw: string; }'.
|
|
21
|
+
raws.value && raws.value.prefix
|
|
22
|
+
].reduce((count, str) => {
|
|
23
|
+
if (str) {
|
|
24
|
+
return count + str.length
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return count
|
|
28
|
+
}, 0)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @param {import('postcss').Declaration} decl
|
|
33
|
+
* @returns {string}
|
|
34
|
+
*/
|
|
35
|
+
function getDeclarationValue (decl) {
|
|
36
|
+
const raws = decl.raws
|
|
37
|
+
|
|
38
|
+
return (raws.value && raws.value.raw) || decl.value
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @param {import('postcss').Declaration} decl
|
|
43
|
+
* @returns {boolean | import('postcss').AtRule}
|
|
44
|
+
*/
|
|
45
|
+
function getMediaQueryAtRule (decl) {
|
|
46
|
+
if (decl.parent && decl.parent.parent && isAtRule(decl.parent.parent)) {
|
|
47
|
+
const parent = decl.parent.parent
|
|
48
|
+
|
|
49
|
+
return parent.name === 'media' ? parent : false
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return false
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* @param {Node} node
|
|
57
|
+
* @returns {node is import('postcss').AtRule}
|
|
58
|
+
*/
|
|
59
|
+
function isAtRule (node) {
|
|
60
|
+
return node.type === 'atrule'
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Check whether a property is a custom one
|
|
65
|
+
* @param {string} property
|
|
66
|
+
* @returns {boolean}
|
|
67
|
+
*/
|
|
68
|
+
function isCustomProperty (property) {
|
|
69
|
+
return property.startsWith('--')
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Check whether a property is standard
|
|
74
|
+
*
|
|
75
|
+
* @param {string} property
|
|
76
|
+
* @return {boolean} If `true`, the property is standard
|
|
77
|
+
*/
|
|
78
|
+
|
|
79
|
+
function isStandardSyntaxProperty (property) {
|
|
80
|
+
// SCSS var (e.g. $var: x), list (e.g. $list: (x)) or map (e.g. $map: (key:value))
|
|
81
|
+
if (property.startsWith('$')) {
|
|
82
|
+
return false
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Less var (e.g. @var: x)
|
|
86
|
+
if (property.startsWith('@')) {
|
|
87
|
+
return false
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// SCSS or Less interpolation
|
|
91
|
+
if (/#{.+?}|@{.+?}|\$\(.+?\)/.test(property)) {
|
|
92
|
+
return false
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return true
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* @param {import('postcss').Declaration} decl
|
|
100
|
+
* @returns {boolean}
|
|
101
|
+
*/
|
|
102
|
+
function isInColorGamutP3MediaQuery (decl) {
|
|
103
|
+
const mediaQueryAtRule = getMediaQueryAtRule(decl)
|
|
104
|
+
|
|
105
|
+
return mediaQueryAtRule && mediaQueryAtRule.params === '(color-gamut: p3)'
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* @param {import('postcss').Declaration} decl
|
|
110
|
+
* @returns {boolean}
|
|
111
|
+
*/
|
|
112
|
+
function isInColorGamutRec2020MediaQuery (decl) {
|
|
113
|
+
const mediaQueryAtRule = getMediaQueryAtRule(decl)
|
|
114
|
+
|
|
115
|
+
return mediaQueryAtRule && mediaQueryAtRule.params === '(color-gamut: rec2020)'
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
module.exports = {
|
|
119
|
+
declarationValueIndex,
|
|
120
|
+
getDeclarationValue,
|
|
121
|
+
getMediaQueryAtRule,
|
|
122
|
+
isCustomProperty,
|
|
123
|
+
isStandardSyntaxProperty,
|
|
124
|
+
isAtRule,
|
|
125
|
+
isInColorGamutP3MediaQuery,
|
|
126
|
+
isInColorGamutRec2020MediaQuery
|
|
127
|
+
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Get the index of a declaration's value
|
|
5
|
-
*
|
|
6
|
-
* @param {import('postcss').Declaration} decl
|
|
7
|
-
* @returns {number}
|
|
8
|
-
*/
|
|
9
|
-
module.exports = function declarationValueIndex (decl) {
|
|
10
|
-
const raws = decl.raws
|
|
11
|
-
|
|
12
|
-
return [
|
|
13
|
-
// @ts-expect-error -- TS2571: Object is of type 'unknown'.
|
|
14
|
-
raws.prop && raws.prop.prefix,
|
|
15
|
-
// @ts-expect-error -- TS2571: Object is of type 'unknown'.
|
|
16
|
-
(raws.prop && raws.prop.raw) || decl.prop,
|
|
17
|
-
// @ts-expect-error -- TS2571: Object is of type 'unknown'.
|
|
18
|
-
raws.prop && raws.prop.suffix,
|
|
19
|
-
raws.between || ':',
|
|
20
|
-
// @ts-expect-error -- TS2339: Property 'prefix' does not exist on type '{ value: string; raw: string; }'.
|
|
21
|
-
raws.value && raws.value.prefix
|
|
22
|
-
].reduce((count, str) => {
|
|
23
|
-
if (str) {
|
|
24
|
-
return count + str.length
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return count
|
|
28
|
-
}, 0)
|
|
29
|
-
}
|
package/src/utils/isAtRule.js
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Check whether a property is standard
|
|
3
|
-
*
|
|
4
|
-
* @param {string} property
|
|
5
|
-
* @return {boolean} If `true`, the property is standard
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
module.exports = function isStandardSyntaxProperty (property) {
|
|
9
|
-
// SCSS var (e.g. $var: x), list (e.g. $list: (x)) or map (e.g. $map: (key:value))
|
|
10
|
-
if (property.startsWith('$')) {
|
|
11
|
-
return false
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
// Less var (e.g. @var: x)
|
|
15
|
-
if (property.startsWith('@')) {
|
|
16
|
-
return false
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// SCSS or Less interpolation
|
|
20
|
-
if (/#{.+?}|@{.+?}|\$\(.+?\)/.test(property)) {
|
|
21
|
-
return false
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
return true
|
|
25
|
-
}
|