1+ var matchRecursive = require ( 'match-recursive' ) ;
2+
13var generateScopeList = require ( './generate-scope-list' ) ;
24var isNodeUnderScope = require ( './is-node-under-scope' ) ;
35var gatherVariableDependencies = require ( './gather-variable-dependencies' ) ;
46
57var findNodeAncestorWithSelector = require ( './find-node-ancestor-with-selector' ) ;
68var cloneSpliceParentOntoNodeWhen = require ( './clone-splice-parent-onto-node-when' ) ;
79
8-
9-
10- // var() = var( <custom-property-name> [, <any-value> ]? )
11- // matches `name[, fallback]`, captures "name" and "fallback"
12- // See: http://dev.w3.org/csswg/css-variables/#funcdef-var
13- var RE_VAR_FUNC = ( / v a r \( \s * ( - - [ ^ , \s ] + ?) (?: \s * , \s * ( [ ^ ( ) ] + | .+ ) ) ? \s * \) / ) ;
10+ // regex to capture variable names
11+ var RE_VAR_FUNC = ( / v a r \( \s * ( - - [ ^ , \s ) ] + ) / ) ;
1412
1513function toString ( value ) {
1614 return String ( value ) ;
@@ -32,37 +30,42 @@ var resolveValue = function(decl, map, /*optional*/ignorePseudoScope, /*internal
3230
3331 var variablesUsedInValueMap = { } ;
3432 // Use `replace` as a loop to go over all occurrences with the `g` flag
35- var resultantValueCopy = resultantValue ;
36- while ( resultantValueCopy . match ( new RegExp ( RE_VAR_FUNC . source , 'g' ) ) ) {
37- resultantValueCopy = resultantValueCopy . replace ( new RegExp ( RE_VAR_FUNC . source , 'g' ) , function ( match , variableName , fallback ) {
38- variablesUsedInValueMap [ variableName ] = true ;
39- } ) ;
40- }
41-
33+ resultantValue . replace ( new RegExp ( RE_VAR_FUNC . source , 'g' ) , function ( match , variableName , fallback ) {
34+ variablesUsedInValueMap [ variableName ] = true ;
35+ } ) ;
4236 var variablesUsedInValue = Object . keys ( variablesUsedInValueMap ) ;
4337
4438 //console.log(debugIndent, (_debugIsInternal ? '' : 'Try resolving'), generateScopeList(decl.parent, true), `ignorePseudoScope=${ignorePseudoScope}`, '------------------------');
4539
4640 // Resolve any var(...) substitutons
4741 var isResultantValueUndefined = false ;
42+
43+ // var() = var( <custom-property-name> [, <any-value> ]? )
44+ // matches `name[, fallback]`, captures "name" and "fallback"
45+ // See: http://dev.w3.org/csswg/css-variables/#funcdef-var
46+ var matches = null ;
47+ // iterate over possible recursive matches
48+ while ( ( matches = matchRecursive ( resultantValue , 'var(...)' ) ) . length ) {
49+ // var originalDeclaration = matches.shift(); // get what was matched
50+
51+ // iterate over found var() occurences (some declaration may have multiple var() nested)
52+ matches . forEach ( function ( match ) {
53+ match = match . split ( ',' ) ;
54+ match = [ match . shift ( ) , match . join ( ',' ) ] . filter ( item => item ) // clear empty items
55+
56+ var [ variableName , fallback ] = match . map ( item => item . trim ( ) )
4857
49- while ( resultantValue . match ( new RegExp ( RE_VAR_FUNC . source , 'g' ) ) ) {
50- resultantValue = resultantValue . replace ( new RegExp ( RE_VAR_FUNC . source , 'g' ) , function ( match , variableName , fallback ) {
51- // Loop through the list of declarations for that value and find the one that best matches
52- // By best match, we mean, the variable actually applies. Criteria:
53- // - is under the same scope
54- // - The latest defined `!important` if any
5558 var matchingVarDeclMapItem ;
5659 ( map [ variableName ] || [ ] ) . forEach ( function ( varDeclMapItem ) {
5760 // Make sure the variable declaration came from the right spot
5861 // And if the current matching variable is already important, a new one to replace it has to be important
5962 var isRoot = varDeclMapItem . parent . type === 'root' || varDeclMapItem . parent . selectors [ 0 ] === ':root' ;
60-
63+
6164 var underScope = isNodeUnderScope ( decl . parent , varDeclMapItem . parent ) ;
6265 var underScsopeIgnorePseudo = isNodeUnderScope ( decl . parent , varDeclMapItem . parent , ignorePseudoScope ) ;
63-
66+
6467 //console.log(debugIndent, 'isNodeUnderScope', underScope, underScsopeIgnorePseudo, generateScopeList(varDeclMapItem.parent, true), varDeclMapItem.decl.value);
65-
68+
6669 if (
6770 underScsopeIgnorePseudo &&
6871 // And if the currently matched declaration is `!important`, it will take another `!important` to override it
@@ -71,7 +74,7 @@ var resolveValue = function(decl, map, /*optional*/ignorePseudoScope, /*internal
7174 matchingVarDeclMapItem = varDeclMapItem ;
7275 }
7376 } ) ;
74-
77+
7578 // Default to the calculatedInPlaceValue which might be a previous fallback, then try this declarations fallback
7679 var replaceValue = ( matchingVarDeclMapItem || { } ) . calculatedInPlaceValue || ( function ( ) {
7780 // Resolve `var` values in fallback
@@ -80,32 +83,33 @@ var resolveValue = function(decl, map, /*optional*/ignorePseudoScope, /*internal
8083 var fallbackDecl = decl . clone ( { parent : decl . parent , value : fallback } ) ;
8184 fallbackValue = resolveValue ( fallbackDecl , map , false , /*internal*/ true ) . value ;
8285 }
83-
86+
8487 return fallbackValue ;
8588 } ) ( ) ;
8689 // Otherwise if the dependency health is good(no circular or self references), dive deeper and resolve
8790 if ( matchingVarDeclMapItem !== undefined && ! gatherVariableDependencies ( variablesUsedInValue , map ) . hasCircularOrSelfReference ) {
8891 // Splice the declaration parent onto the matching entry
89-
92+
9093 var varDeclScopeList = generateScopeList ( decl . parent . parent , true ) ;
9194 var innerMostAtRuleSelector = varDeclScopeList [ 0 ] . slice ( - 1 ) [ 0 ] ;
9295 var nodeToSpliceParentOnto = findNodeAncestorWithSelector ( innerMostAtRuleSelector , matchingVarDeclMapItem . decl . parent ) ;
9396 // See: `test/fixtures/cascade-with-calc-expression-on-nested-rules`
9497 var matchingMimicDecl = cloneSpliceParentOntoNodeWhen ( matchingVarDeclMapItem . decl , decl . parent . parent , function ( ancestor ) {
9598 return ancestor === nodeToSpliceParentOnto ;
9699 } ) ;
97-
100+
98101 replaceValue = resolveValue ( matchingMimicDecl , map , false , /*internal*/ true ) . value ;
99102 }
100-
103+
101104 isResultantValueUndefined = replaceValue === undefined ;
102105 if ( isResultantValueUndefined ) {
103106 warnings . push ( [ 'variable ' + variableName + ' is undefined and used without a fallback' , { node : decl } ] ) ;
104107 }
105-
108+
109+ // replace original declaration
110+ resultantValue = resultantValue . replace ( `var(${ match . join ( ',' ) } )` , replaceValue )
111+
106112 //console.log(debugIndent, 'replaceValue', replaceValue);
107-
108- return replaceValue ;
109113 } ) ;
110114 }
111115
0 commit comments