@@ -29,20 +29,20 @@ const extractVarsFromSLDS = (props = {}) => {
29
29
// getting list of components and utilities to parse
30
30
const componentList = fs
31
31
. readdirSync ( './.generated/css/components' )
32
- . map ( name => `./.generated/css/components/${ name } /**/index.css` ) ;
32
+ . map ( ( name ) => `./.generated/css/components/${ name } /**/index.css` ) ;
33
33
const utilityList = fs
34
34
. readdirSync ( './.generated/css/utilities' )
35
- . map ( name => `./.generated/css/utilities/${ name } /**/index.css` ) ;
35
+ . map ( ( name ) => `./.generated/css/utilities/${ name } /**/index.css` ) ;
36
36
37
37
// parse through all variants of each component / util
38
- componentList . concat ( utilityList ) . map ( fileGlob => {
38
+ componentList . concat ( utilityList ) . map ( ( fileGlob ) => {
39
39
const cssFiles = glob . sync ( fileGlob ) ;
40
40
let varsData = { } ;
41
41
42
- cssFiles . map ( filename => {
42
+ cssFiles . map ( ( filename ) => {
43
43
const cssContent = readFileSync ( filename ) . toString ( ) ;
44
44
const fileVars = extractVarsFromCSS ( cssContent , {
45
- allowPattern : varsAllowPattern
45
+ allowPattern : varsAllowPattern ,
46
46
} ) ;
47
47
48
48
if ( Object . keys ( fileVars ) . length > 0 ) {
@@ -73,12 +73,12 @@ const extractVarsFromSLDS = (props = {}) => {
73
73
*/
74
74
const extractVarsFromCSS = ( cssContent , options = { } ) => {
75
75
const ast = css . parse ( cssContent ) ;
76
- const rules = ast . stylesheet . rules . filter ( rule => rule . type === 'rule' ) ;
76
+ const rules = ast . stylesheet . rules . filter ( ( rule ) => rule . type === 'rule' ) ;
77
77
const { allowPattern } = options ;
78
78
let list = { } ;
79
79
80
- rules . map ( rule => {
81
- const filtered = rule . declarations . filter ( dec => {
80
+ rules . map ( ( rule ) => {
81
+ const filtered = rule . declarations . filter ( ( dec ) => {
82
82
if ( ! dec . value ) return false ;
83
83
84
84
// match on var values that are not custom props
@@ -89,49 +89,102 @@ const extractVarsFromCSS = (cssContent, options = {}) => {
89
89
return false ;
90
90
} ) ;
91
91
92
- if ( filtered . length > 0 ) {
93
- const vars = rule . declarations
94
- . filter ( dec => ( dec . value ? dec . value . match ( / v a r \( / ) : false ) )
95
- . map ( dec => {
96
- const cssVar = dec . value . match ( / ( - - .* ?) [ , | ) ] / ) [ 1 ] ;
97
- const fallbackMatch = dec . value . match ( / , \s ( .* ) \) / ) ;
98
- const fallback = fallbackMatch ? fallbackMatch [ 1 ] : '' ;
99
-
100
- return {
101
- [ cssVar ] : fallback
102
- } ;
103
- } ) ;
104
-
105
- vars . forEach ( cssVar => {
106
- const varName = Object . keys ( cssVar ) [ 0 ] ;
107
- if ( varName ) {
108
- const categories = Object . keys ( propTypes ) ;
109
-
110
- const matchedCategory = categories . find ( option => {
111
- if ( varName . includes ( option ) ) {
112
- return option ;
113
- }
114
- } ) ;
92
+ ///
93
+ // is this a CSS variable
94
+ function isCssVarClause ( value ) {
95
+ // if it DOES contain `var(` it is a CSS variable clause
96
+ return ! ! value . includes ( 'var(' ) ;
97
+ }
115
98
116
- list [ varName ] = { fallbackValue : cssVar [ varName ] } ;
99
+ // is this a CSS variable reassignment
100
+ function isCssVarReassignment ( value ) {
101
+ // if it DOES NOT contain a comma it is a reassignment
102
+ return ! value . includes ( ',' ) ;
103
+ }
104
+
105
+ // regex:
106
+ // var\( -> find something that starts with `var(`
107
+ // (?=\S*['-]*) -> positive lookahead, zero or more non-space characters and zero or more ' or - characters
108
+ // ([a-zA-Z'-]+) -> match group 1, one or more alpha characters
109
+ // \s*,\s* -> zero or more spaces, a comma, zero or more spaces
110
+ // (.+) -> match group 2, one or more of any character
111
+ // \) -> a closing parenthesis
112
+ const varRegex = / v a r \( (? = \S * [ ' - ] * ) ( [ a - z A - Z ' - ] + ) \s * [ , ] ? \s * ( .* ) \) / ;
113
+
114
+ /**
115
+ * Recursively drill into a CSS value to find the fallback value
116
+ *
117
+ * @param {string } - CSS property's value
118
+ * @returns {object } - hookName, name of the hook; fallback, value of the fallback
119
+ */
120
+ function findFallbackRecursively ( value ) {
121
+ // get the fallback value from the CSS variable clause
122
+ let regex = varRegex ;
123
+ let matches = value . match ( regex ) ;
124
+
125
+ if ( matches ) {
126
+ let fallbackValue = matches [ 2 ] ;
127
+
128
+ // if we have another CSS variable clause then keep drilling in
129
+ if ( isCssVarClause ( fallbackValue ) ) {
130
+ return findFallbackRecursively ( fallbackValue ) ;
131
+ } else {
132
+ return matches [ 2 ] . trim ( ) ;
133
+ }
134
+ }
117
135
118
- if ( matchedCategory ) {
119
- list [ varName ] . category = propTypes [ matchedCategory ] . type ;
120
- list [ varName ] . valueType = propTypes [
121
- matchedCategory
122
- ] . valueTypes . join ( '' ) ;
136
+ // if we have a reassignment
137
+ if ( isCssVarReassignment ( value ) ) {
138
+ return null ;
139
+ }
140
+
141
+ return value ;
142
+ }
143
+ ///
144
+
145
+ if ( filtered . length > 0 ) {
146
+ filtered . forEach ( ( decl ) => {
147
+ if ( decl . value ) {
148
+ // search for a `var(...)` value
149
+ let matches = decl . value . match ( varRegex ) ;
150
+ // if we found a match then extract the hook name, if no hook then return null
151
+ let hookName = matches ? matches [ 1 ] : null ;
152
+ // recursively search for the fallback value
153
+ let fallback = findFallbackRecursively ( decl . value ) ;
154
+
155
+ // if we have a hook then generate an object to add to the list of results
156
+ if ( hookName ) {
157
+ // retrieve the category names from the JSON config
158
+ const categories = Object . keys ( propTypes ) ;
159
+
160
+ // do we have a category match? If so return it
161
+ const matchedCategory = categories . find ( ( option ) => {
162
+ if ( hookName . includes ( option ) ) {
163
+ return option ;
164
+ }
165
+ } ) ;
166
+
167
+ // add the fallback value to the hook's object in the list of results
168
+ list [ hookName ] = { fallbackValue : fallback } ;
169
+
170
+ // if a category was found add the metadata to the hook's object in the list of results
171
+ if ( matchedCategory ) {
172
+ list [ hookName ] . category = propTypes [ matchedCategory ] . type ;
173
+ list [ hookName ] . valueType =
174
+ propTypes [ matchedCategory ] . valueTypes . join ( ', ' ) ;
175
+ }
123
176
}
124
177
}
125
178
} ) ;
126
179
}
127
180
} ) ;
128
181
129
182
return allowPattern
130
- ? filterObject ( list , key => RegExp ( allowPattern ) . test ( key ) )
183
+ ? filterObject ( list , ( key ) => RegExp ( allowPattern ) . test ( key ) )
131
184
: list ;
132
185
} ;
133
186
134
187
module . exports = {
135
188
extractVarsFromCSS,
136
- extractVarsFromSLDS
189
+ extractVarsFromSLDS,
137
190
} ;
0 commit comments