@@ -4,11 +4,13 @@ const specificityLib = require('specificity');
4
4
const generateSelectorBranchesFromPostcssNode = require ( 'postcss-node-scope-utility/lib/generate-branches' ) ;
5
5
const isSelectorBranchUnderScope = require ( 'postcss-node-scope-utility/lib/is-branch-under-scope' ) ;
6
6
7
- // A custom property is any property whose name starts with two dashes (U+002D HYPHEN-MINUS)
8
- // `--foo`
9
- // See: http://dev.w3.org/csswg/css-variables/#custom-property
10
- const RE_VAR_PROP = ( / ( - - ( .+ ) ) / ) ;
11
- const RE_VAR_FUNC = ( / v a r \( ( - - [ ^ , \s ] + ?) (?: \s * , \s * ( .+ ) ) ? \) / ) ;
7
+ const {
8
+ RE_VAR_PROP ,
9
+ RE_VAR_FUNC
10
+ } = require ( './lib/var-regex' ) ;
11
+ const expandVarUsageFromVarDefinition = require ( './lib/expand-var-usage-from-var-definition' ) ;
12
+
13
+
12
14
13
15
function getSpecificity ( selector ) {
14
16
// We only care about the first piece because we have already split the comma-separated pieces before we use this
@@ -33,30 +35,6 @@ function eachCssVariableDeclaration(css, cb) {
33
35
} ) ;
34
36
}
35
37
36
- function cloneParentAncestry ( node ) {
37
- const clone = node . clone ( ) ;
38
- clone . removeAll ( ) ;
39
-
40
- if ( node . parent && node . parent . type !== 'root' ) {
41
- const parentClone = node . parent . clone ( ) ;
42
- parentClone . removeAll ( ) ;
43
- parentClone . append ( clone ) ;
44
-
45
- return cloneParentAncestry ( parentClone ) ;
46
- }
47
-
48
- return clone ;
49
- }
50
-
51
- function insertNodeIntoAncestry ( ancestry , insertNode ) {
52
- let deepestNode = ancestry ;
53
- while ( ! deepestNode . nodes || deepestNode . nodes . length > 0 ) {
54
- deepestNode = deepestNode . nodes [ 0 ] ;
55
- }
56
-
57
- deepestNode . append ( insertNode ) ;
58
- }
59
-
60
38
61
39
var defaults = {
62
40
// Allows you to preserve custom properties & var() usage in output.
@@ -117,22 +95,25 @@ module.exports = postcss.plugin('postcss-css-variables', function(options) {
117
95
// Collect all of the variables defined `--foo: #f00;`
118
96
// ---------------------------------------------------------
119
97
// ---------------------------------------------------------
98
+ const usageDeclsToSkip = [ ] ;
120
99
eachCssVariableDeclaration ( css , function ( variableDecl ) {
121
100
// We cache the parent rule because after decl removal, it will be undefined
122
101
const variableDeclParentRule = variableDecl . parent ;
123
102
const variableName = variableDecl . prop ;
124
103
const variableValue = variableDecl . value ;
125
104
const isImportant = variableDecl . important || false ;
126
- const variableSelectorBranchs = generateSelectorBranchesFromPostcssNode ( variableDeclParentRule ) ;
105
+ const variableSelectorBranches = generateSelectorBranchesFromPostcssNode ( variableDeclParentRule ) ;
127
106
128
107
debug ( `Collecting ${ variableName } =${ variableValue } isImportant=${ isImportant } from ${ variableDeclParentRule . selector . toString ( ) } ` ) ;
129
108
130
- map [ variableName ] = ( map [ variableName ] || [ ] ) . concat ( {
109
+ const variableEntry = {
131
110
name : variableName ,
132
111
value : variableValue ,
133
112
isImportant,
134
- selectorBranches : variableSelectorBranchs
135
- } ) ;
113
+ selectorBranches : variableSelectorBranches
114
+ } ;
115
+
116
+ map [ variableName ] = ( map [ variableName ] || [ ] ) . concat ( variableEntry ) ;
136
117
137
118
138
119
// Expand/rollout/unroll variable usage
@@ -146,69 +127,26 @@ module.exports = postcss.plugin('postcss-css-variables', function(options) {
146
127
// .foo:hover { --color: #0f0; color: var(--color); }
147
128
// --------------------------------
148
129
css . walkDecls ( function ( usageDecl ) {
149
- // Avoid duplicating the usage decl on itself
150
- // And make sure this decl has `var()` usage
151
- if ( variableDeclParentRule === usageDecl . parent || ! RE_VAR_FUNC . test ( usageDecl . value ) ) {
130
+ if (
131
+ // Avoid iterating a var declaration `--var: #f00`
132
+ RE_VAR_PROP . test ( usageDecl . prop ) ||
133
+ // Make sure this decl has `var()` usage
134
+ ! RE_VAR_FUNC . test ( usageDecl . value ) ||
135
+ // Avoid duplicating the usage decl on itself
136
+ variableDeclParentRule === usageDecl . parent ||
137
+ // Avoid iterating a clone we may have just added before
138
+ usageDeclsToSkip . some ( ( skipDecl ) => skipDecl === usageDecl )
139
+ ) {
152
140
return ;
153
141
}
154
142
155
- const usageSelectorBranches = generateSelectorBranchesFromPostcssNode ( usageDecl . parent ) ;
156
-
157
- variableSelectorBranchs . some ( ( variableSelectorBranch ) => {
158
- return usageSelectorBranches . some ( ( usageSelectorBranch ) => {
159
- // In this case, we look whether the usage is under the scope of the definition
160
- const isUnderScope = isSelectorBranchUnderScope ( usageSelectorBranch , variableSelectorBranch ) ;
161
-
162
- debug ( `Should unroll decl? isUnderScope=${ isUnderScope } ` , usageSelectorBranch . selector . toString ( ) , '|' , variableSelectorBranch . selector . toString ( ) )
163
-
164
- // For general cases, we can put variable usage right-below the variable definition
165
- if ( isUnderScope ) {
166
- usageDecl . value . replace ( new RegExp ( RE_VAR_FUNC . source , 'g' ) , ( match , matchedVariableName ) => {
167
- if ( matchedVariableName === variableName ) {
168
- variableDecl . after ( usageDecl . clone ( ) ) ;
169
- }
170
- } ) ;
171
- }
172
- // For at-rules
173
- else {
174
- // If there is a conditional like a atrule/media-query, then we should check whether
175
- // the variable can apply and put our usage within that same context
176
- // Before:
177
- // :root { --color: #f00; }
178
- // @media (max-width: 1000px) { :root { --color: #0f0; } }
179
- // .box { color: var(--color); }
180
- // After:
181
- // .box { color: #f00; }
182
- // @media (max-width: 1000px) {.box { color: #0f0; } }
183
- const hasAtRule = ( variableSelectorBranch . conditionals || [ ] ) . some ( ( conditional ) => {
184
- return conditional . type === 'atrule' ;
185
- } )
186
- if ( hasAtRule ) {
187
- const doesVariableApplyToUsage = isSelectorBranchUnderScope ( variableSelectorBranch , usageSelectorBranch , { ignoreConditionals : true } ) ;
188
- debug ( `Should expand atrule? doesVariableApplyToUsage=${ doesVariableApplyToUsage } ` , variableSelectorBranch . selector . toString ( ) , '|' , usageSelectorBranch . selector . toString ( ) )
189
-
190
- if ( doesVariableApplyToUsage ) {
191
- // Create a new usage clone with only the usage decl
192
- const newUsageRule = usageDecl . parent . clone ( ) ;
193
- newUsageRule . removeAll ( ) ;
194
- newUsageRule . append ( usageDecl . clone ( ) ) ;
195
-
196
- const variableAncestry = cloneParentAncestry ( variableDecl . parent . parent ) ;
197
- insertNodeIntoAncestry ( variableAncestry , newUsageRule ) ;
198
-
199
- usageDecl . parent . cloneBefore ( ) ;
200
- usageDecl . parent . replaceWith ( variableAncestry ) ;
201
- }
202
- }
203
- }
204
-
205
-
206
- return isUnderScope ;
207
- } ) ;
208
- } ) ;
143
+ const newUsageDecl = expandVarUsageFromVarDefinition ( variableEntry , variableDecl , usageDecl ) ;
144
+ // Keep track of the cloned decls we should skip over
145
+ usageDeclsToSkip . push ( newUsageDecl ) ;
209
146
} ) ;
210
147
211
148
149
+
212
150
// Remove the variable declaration because they are pretty much useless after we resolve them
213
151
if ( ! opts . preserve ) {
214
152
variableDecl . remove ( ) ;
@@ -270,7 +208,7 @@ module.exports = postcss.plugin('postcss-css-variables', function(options) {
270
208
const isUnderScope = isSelectorBranchUnderScope ( variableSelectorBranch , selectorBranch ) ;
271
209
const specificity = getSpecificity ( variableSelectorBranch . selector . toString ( ) ) ;
272
210
273
- debug ( `isUnderScope=${ isUnderScope } compareSpecificity=${ compareSpecificity ( specificity , currentGreatestSpecificity ) } specificity=${ specificity } ` , variableSelectorBranch . selector . toString ( ) , '|' , selectorBranch . selector . toString ( ) )
211
+ debug ( `isUnderScope=${ isUnderScope } compareSpecificity=${ compareSpecificity ( specificity , currentGreatestSpecificity ) } specificity=${ specificity } ` , variableSelectorBranch . selector . toString ( ) , '|' , selectorBranch . selector . toString ( ) , '-->' , variableEntry . value )
274
212
275
213
if ( isUnderScope && compareSpecificity ( specificity , currentGreatestSpecificity ) >= 0 ) {
276
214
currentGreatestSpecificity = specificity ;
0 commit comments