1
- import postcss from 'postcss' ;
2
- import parser from 'postcss-values-parser' ;
3
- import { lab2rgb } from '@csstools/convert-colors' ;
4
-
5
- export default postcss . plugin ( 'postcss-color-gray' , opts => root => {
6
- // walk all declarations likely containing a gray() function
7
- root . walkDecls ( decl => {
8
- if ( hasGrayFunction ( decl ) ) {
9
- const { value : originalValue } = decl ;
10
-
11
- // parse the declaration value
12
- const ast = parser ( originalValue ) . parse ( ) ;
13
-
14
- // walk every node in the value that contains a gray() function
15
- ast . walk ( node => {
16
- const [ lightness , alpha ] = getFunctionGrayArgs ( node ) ;
17
-
18
- if ( lightness !== undefined ) {
19
- // rename the gray() function to rgb()
20
- node . value = 'rgb' ;
21
-
22
- // convert the lab gray lightness into rgb
23
- const [ r , g , b ] = lab2rgb ( lightness , 0 , 0 ) . map (
24
- channel => Math . max ( Math . min ( Math . round ( channel * 2.55 ) , 255 ) , 0 )
25
- ) ;
26
-
27
- // preserve the slash nodes within rgb()
28
- const openingSlash = node . first ;
29
- const closingSlash = node . last ;
30
-
31
- node . removeAll ( )
32
- // replace the contents of rgb with `(r,g,b`
33
- . append ( openingSlash )
34
- . append ( parser . number ( { value : r } ) )
35
- . append ( parser . comma ( { value : ',' } ) )
36
- . append ( parser . number ( { value : g } ) )
37
- . append ( parser . comma ( { value : ',' } ) )
38
- . append ( parser . number ( { value : b } ) )
39
-
40
- // if an alpha channel was defined
41
- if ( alpha < 1 ) {
42
- // rename the rgb() function to rgba()
43
- node . value += 'a' ;
44
-
45
- node
46
- // append the contents of rgba with `,a`
47
- . append ( parser . comma ( { value : ',' } ) )
48
- . append ( parser . number ( { value : alpha } ) ) ;
1
+ const { parse} = require ( "postcss-values-parser" ) ;
2
+ const { lab2rgb} = require ( "@csstools/convert-colors" ) ;
3
+ const Numeric = require ( "postcss-values-parser/lib/nodes/Numeric" ) ;
4
+ const Punctuation = require ( "postcss-values-parser/lib/nodes/Punctuation" ) ;
5
+
6
+ /**
7
+ * @param {{preserve?: boolean} } opts
8
+ * @returns {import('postcss').Plugin }
9
+ */
10
+ module . exports = function creator ( opts ) {
11
+ const preserve = Boolean ( Object ( opts ) . preserve ) ;
12
+
13
+ return {
14
+ postcssPlugin : 'postcss-color-gray' ,
15
+ // walk all declarations likely containing a gray() function
16
+ Declaration ( decl ) {
17
+ if ( hasGrayFunction ( decl ) ) {
18
+ const { value : originalValue } = decl ;
19
+
20
+ // parse the declaration value
21
+ const ast = parse ( originalValue ) ;
22
+
23
+ // walk every node in the value that contains a gray() function
24
+ ast . walkFuncs ( node => {
25
+ const [ lightness , alpha ] = getFunctionGrayArgs ( node ) ;
26
+
27
+ if ( lightness !== undefined ) {
28
+ // rename the gray() function to rgb()
29
+ node . name = 'rgb' ;
30
+
31
+ // convert the lab gray lightness into rgb
32
+ const [ r , g , b ] = lab2rgb ( lightness , 0 , 0 ) . map (
33
+ channel => Math . max ( Math . min ( Math . round ( channel * 2.55 ) , 255 ) , 0 )
34
+ ) ;
35
+
36
+ node . removeAll ( )
37
+ // replace the contents of rgb with `r,g,b`
38
+ . append ( new Numeric ( { value : r } ) )
39
+ . append ( new Punctuation ( { value : ',' } ) )
40
+ . append ( new Numeric ( { value : g } ) )
41
+ . append ( new Punctuation ( { value : ',' } ) )
42
+ . append ( new Numeric ( { value : b } ) )
43
+
44
+ // if an alpha channel was defined
45
+ if ( alpha < 1 ) {
46
+ // rename the rgb() function to rgba()
47
+ node . name += 'a' ;
48
+
49
+ node
50
+ // append the contents of rgba with `,a`
51
+ . append ( new Punctuation ( { value : ',' } ) )
52
+ . append ( new Numeric ( { value : alpha } ) ) ;
53
+ }
54
+ }
55
+ } ) ;
56
+
57
+ const modifiedValue = ast . toString ( ) ;
58
+
59
+ // if the modified value has changed from the original value
60
+ if ( originalValue !== modifiedValue ) {
61
+ // if the original gray() color is to be preserved
62
+ if ( preserve ) {
63
+ // insert the declaration value with the fallback before the current declaration
64
+ decl . cloneBefore ( {
65
+ value : modifiedValue
66
+ } ) ;
67
+ } else {
68
+ // otherwise, overwrite the declaration value with the fallback
69
+ decl . value = modifiedValue ;
49
70
}
50
-
51
- // append the contents of rgb/rgba with `)`
52
- node . append ( closingSlash ) ;
53
- }
54
- } ) ;
55
-
56
- const modifiedValue = ast . toString ( ) ;
57
-
58
- // if the modified value has changed from the original value
59
- if ( originalValue !== modifiedValue ) {
60
- // if the original gray() color is to be preserved
61
- if ( Object ( opts ) . preserve ) {
62
- // insert the declaration value with the fallback before the current declaration
63
- decl . cloneBefore ( {
64
- value : modifiedValue
65
- } ) ;
66
- } else {
67
- // otherwise, overwrite the declaration value with the fallback
68
- decl . value = modifiedValue ;
69
71
}
70
72
}
71
73
}
72
- } ) ;
73
- } ) ;
74
+ }
75
+ }
76
+
77
+ module . exports . postcss = true ;
74
78
75
79
// return whether a string contains a gray() function
76
80
const hasGrayFunctionRegExp = / ( ^ | [ ^ \w - ] ) g r a y \( / i;
77
81
const hasGrayFunction = decl => hasGrayFunctionRegExp . test ( Object ( decl ) . value ) ;
78
82
79
83
// return whether a node matches a specific type
80
- const isNumber = node => Object ( node ) . type === 'number ' ;
84
+ const isNumber = node => Object ( node ) . type === 'numeric ' ;
81
85
const isOperator = node => Object ( node ) . type === 'operator' ;
82
86
const isFunction = node => Object ( node ) . type === 'func' ;
83
- const isCalcRegExp = / ^ c a l c $ / i;
84
- const isFunctionCalc = node => isFunction ( node ) && isCalcRegExp . test ( node . value ) ;
85
- const isGrayRegExp = / ^ g r a y $ / i;
86
- const isFunctionGrayWithArgs = node => isFunction ( node ) && isGrayRegExp . test ( node . value ) && node . nodes && node . nodes . length ;
87
+ const isFunctionCalc = node => isFunction ( node ) && node . name === 'calc' ;
87
88
const isNumberPercentage = node => isNumber ( node ) && node . unit === '%' ;
88
89
const isNumberUnitless = node => isNumber ( node ) && node . unit === '' ;
89
90
const isOperatorSlash = node => isOperator ( node ) && node . value === '/' ;
@@ -105,13 +106,15 @@ const getFunctionGrayArgs = node => {
105
106
const validArgs = [ ] ;
106
107
107
108
// if the node is a gray() function with arguments
108
- if ( isFunctionGrayWithArgs ( node ) ) {
109
+ if ( node . name === 'gray' && node . nodes && node . nodes . length ) {
109
110
// get all the gray() function arguments between `(` and `)`
110
- const nodes = node . nodes . slice ( 1 , - 1 ) ;
111
+ const nodes = node . nodes ;
111
112
112
113
// validate each argument
113
114
for ( const index in nodes ) {
114
- const arg = typeof functionalGrayArgs [ index ] === 'function' ? functionalGrayArgs [ index ] ( nodes [ index ] ) : undefined ;
115
+ const arg = typeof functionalGrayArgs [ index ] === 'function'
116
+ ? functionalGrayArgs [ index ] ( nodes [ index ] )
117
+ : undefined ;
115
118
116
119
// if the argument was validated
117
120
if ( arg !== undefined ) {
0 commit comments