@@ -4,11 +4,13 @@ const specificityLib = require('specificity');
44const generateSelectorBranchesFromPostcssNode = require ( 'postcss-node-scope-utility/lib/generate-branches' ) ;
55const isSelectorBranchUnderScope = require ( 'postcss-node-scope-utility/lib/is-branch-under-scope' ) ;
66
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+
1214
1315function getSpecificity ( selector ) {
1416 // 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) {
3335 } ) ;
3436}
3537
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-
6038
6139var defaults = {
6240 // Allows you to preserve custom properties & var() usage in output.
@@ -117,22 +95,25 @@ module.exports = postcss.plugin('postcss-css-variables', function(options) {
11795 // Collect all of the variables defined `--foo: #f00;`
11896 // ---------------------------------------------------------
11997 // ---------------------------------------------------------
98+ const usageDeclsToSkip = [ ] ;
12099 eachCssVariableDeclaration ( css , function ( variableDecl ) {
121100 // We cache the parent rule because after decl removal, it will be undefined
122101 const variableDeclParentRule = variableDecl . parent ;
123102 const variableName = variableDecl . prop ;
124103 const variableValue = variableDecl . value ;
125104 const isImportant = variableDecl . important || false ;
126- const variableSelectorBranchs = generateSelectorBranchesFromPostcssNode ( variableDeclParentRule ) ;
105+ const variableSelectorBranches = generateSelectorBranchesFromPostcssNode ( variableDeclParentRule ) ;
127106
128107 debug ( `Collecting ${ variableName } =${ variableValue } isImportant=${ isImportant } from ${ variableDeclParentRule . selector . toString ( ) } ` ) ;
129108
130- map [ variableName ] = ( map [ variableName ] || [ ] ) . concat ( {
109+ const variableEntry = {
131110 name : variableName ,
132111 value : variableValue ,
133112 isImportant,
134- selectorBranches : variableSelectorBranchs
135- } ) ;
113+ selectorBranches : variableSelectorBranches
114+ } ;
115+
116+ map [ variableName ] = ( map [ variableName ] || [ ] ) . concat ( variableEntry ) ;
136117
137118
138119 // Expand/rollout/unroll variable usage
@@ -146,69 +127,26 @@ module.exports = postcss.plugin('postcss-css-variables', function(options) {
146127 // .foo:hover { --color: #0f0; color: var(--color); }
147128 // --------------------------------
148129 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+ ) {
152140 return ;
153141 }
154142
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 ) ;
209146 } ) ;
210147
211148
149+
212150 // Remove the variable declaration because they are pretty much useless after we resolve them
213151 if ( ! opts . preserve ) {
214152 variableDecl . remove ( ) ;
@@ -270,7 +208,7 @@ module.exports = postcss.plugin('postcss-css-variables', function(options) {
270208 const isUnderScope = isSelectorBranchUnderScope ( variableSelectorBranch , selectorBranch ) ;
271209 const specificity = getSpecificity ( variableSelectorBranch . selector . toString ( ) ) ;
272210
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 )
274212
275213 if ( isUnderScope && compareSpecificity ( specificity , currentGreatestSpecificity ) >= 0 ) {
276214 currentGreatestSpecificity = specificity ;
0 commit comments