@@ -76,50 +76,99 @@ module.exports = postcss.plugin('postcss-css-variables', function(options) {
7676
7777
7878
79+
7980 // Collect all of the variables defined
8081 // ---------------------------------------------------------
8182 // ---------------------------------------------------------
82- eachCssVariableDeclaration ( css , function ( decl ) {
83+ eachCssVariableDeclaration ( css , function ( variableDecl ) {
8384 // We cache the parent rule because after decl removal, it will be undefined
84- const declParentRule = decl . parent ;
85- const variableName = decl . prop ;
85+ const variableDeclParentRule = variableDecl . parent ;
86+ const variableName = variableDecl . prop ;
87+ const variableValue = variableDecl . value ;
88+ const isImportant = variableDecl . important || false ;
89+ const variableSelectorBranchs = generateSelectorBranchesFromPostcssNode ( variableDeclParentRule ) ;
90+
91+ debug ( `Collecting ${ variableName } =${ variableValue } isImportant=${ isImportant } from ${ variableDeclParentRule . selector . toString ( ) } ` ) ;
8692
8793 map [ variableName ] = ( map [ variableName ] || [ ] ) . concat ( {
8894 name : variableName ,
89- value : decl . value ,
90- isImportant : decl . important || false ,
91- selectorBranches : generateSelectorBranchesFromPostcssNode ( declParentRule )
95+ value : variableValue ,
96+ isImportant,
97+ selectorBranches : variableSelectorBranchs
98+ } ) ;
99+
100+
101+ // Expand/rollout/unroll variable usage
102+ // Where we define variables, also add in any usage that falls under scope
103+ // ex.
104+ // Before:
105+ // .foo { --color: #f00; color: var(--color); }
106+ // .foo:hover { --color: #0f0; };
107+ // After:
108+ // .foo { color: #f00; }
109+ // .foo:hover { color: #0f0; }
110+ // --------------------------------
111+ css . walkDecls ( function ( usageDecl ) {
112+ // Avoid duplicating the usage decl on itself
113+ if ( variableDeclParentRule === usageDecl . parent ) {
114+ return ;
115+ }
116+
117+ const usageSelectorBranches = generateSelectorBranchesFromPostcssNode ( usageDecl . parent ) ;
118+
119+ variableSelectorBranchs . some ( ( variableSelectorBranch ) => {
120+ return usageSelectorBranches . some ( ( usageSelectorBranch ) => {
121+ // In this case, we look whether the usage is under the scope of the definition
122+ const isUnderScope = isSelectorBranchUnderScope ( usageSelectorBranch , variableSelectorBranch ) ;
123+
124+ debug ( `Should expand usage? isUnderScope=${ isUnderScope } ` , usageSelectorBranch . selector . toString ( ) , '|' , variableSelectorBranch . selector . toString ( ) )
125+
126+ if ( isUnderScope ) {
127+ usageDecl . value . replace ( new RegExp ( RE_VAR_FUNC . source , 'g' ) , ( match , matchedVariableName ) => {
128+ if ( matchedVariableName === variableName ) {
129+ variableDecl . after ( usageDecl . clone ( ) ) ;
130+ }
131+ } ) ;
132+ }
133+
134+ return isUnderScope ;
135+ } ) ;
136+ } ) ;
92137 } ) ;
93138
139+
140+
94141 // Remove the variable declaration because they are pretty much useless after we resolve them
95142 if ( ! opts . preserve ) {
96- decl . remove ( ) ;
143+ variableDecl . remove ( ) ;
97144 }
98145 // Or we can also just show the computed value used for that variable
99146 else if ( opts . preserve === 'computed' ) {
100147 // TODO: put computed value here
101148 }
102149
103150 // Clean up the rule that declared them if it doesn't have anything left after we potentially remove the variable decl
104- if ( declParentRule . nodes . length <= 0 ) {
105- declParentRule . remove ( ) ;
151+ if ( variableDeclParentRule . nodes . length <= 0 ) {
152+ variableDeclParentRule . remove ( ) ;
106153 }
107154 } ) ;
108155
109156 debug ( 'map' , map ) ;
110157
158+ debug ( 'After collecting variables ------' ) ;
159+ debug ( css . toString ( ) ) ;
160+ debug ( '---------------------------------' ) ;
111161
112162
113163 // Resolve variables everywhere
114164 // ---------------------------------------------------------
115165 // ---------------------------------------------------------
116166 css . walkDecls ( function ( decl ) {
117- // If it uses variables
118- // and is not a variable declarations that we may be preserving from earlier
167+ // Avoid any variable decls, `--foo: var(--bar);`, that may have been preserved
119168 if ( ! RE_VAR_PROP . test ( decl . prop ) ) {
120169 const selectorBranches = generateSelectorBranchesFromPostcssNode ( decl . parent ) ;
121170
122- decl . value = decl . value . replace ( new RegExp ( RE_VAR_FUNC . source , 'g' ) , ( match , variableName ) => {
171+ decl . value = decl . value . replace ( new RegExp ( RE_VAR_FUNC . source , 'g' ) , ( match , variableName , fallback ) => {
123172 debug ( 'usage' , variableName ) ;
124173 const variableEntries = map [ variableName ] || [ ] ;
125174
@@ -131,10 +180,11 @@ module.exports = postcss.plugin('postcss-css-variables', function(options) {
131180 // We only need to find one branch that matches
132181 variableEntry . selectorBranches . some ( ( variableSelectorBranch ) => {
133182 return selectorBranches . some ( ( selectorBranch ) => {
183+ // Look whether the variable definition is under the scope of the usage
134184 const isUnderScope = isSelectorBranchUnderScope ( variableSelectorBranch , selectorBranch ) ;
135185 const specificity = getSpecificity ( variableSelectorBranch . selector . toString ( ) ) ;
136186
137- debug ( `isUnderScope=${ isUnderScope } compareSpecificity=${ compareSpecificity ( specificity , currentGreatestSpecificity ) } specificity=${ specificity } ` , variableSelectorBranch . selector . toString ( ) , selectorBranch . selector . toString ( ) )
187+ debug ( `isUnderScope=${ isUnderScope } compareSpecificity=${ compareSpecificity ( specificity , currentGreatestSpecificity ) } specificity=${ specificity } ` , variableSelectorBranch . selector . toString ( ) , '|' , selectorBranch . selector . toString ( ) )
138188
139189 if ( isUnderScope && compareSpecificity ( specificity , currentGreatestSpecificity ) >= 0 ) {
140190 currentGreatestSpecificity = specificity ;
@@ -148,7 +198,13 @@ module.exports = postcss.plugin('postcss-css-variables', function(options) {
148198
149199 debug ( 'currentGreatestVariableEntry' , currentGreatestVariableEntry ) ;
150200
151- return currentGreatestVariableEntry . value ;
201+ const resultantValue = ( currentGreatestVariableEntry && currentGreatestVariableEntry . value ) || fallback ;
202+
203+ if ( ! resultantValue ) {
204+ result . warn ( [ 'variable ' + variableName + ' is undefined and used without a fallback' , { node : decl } ] ) ;
205+ }
206+
207+ return resultantValue || 'undefined' ;
152208 } ) ;
153209 }
154210 } ) ;
0 commit comments