1
+ var matchRecursive = require ( 'match-recursive' ) ;
2
+
1
3
var generateScopeList = require ( './generate-scope-list' ) ;
2
4
var isNodeUnderScope = require ( './is-node-under-scope' ) ;
3
5
var gatherVariableDependencies = require ( './gather-variable-dependencies' ) ;
4
6
5
7
var findNodeAncestorWithSelector = require ( './find-node-ancestor-with-selector' ) ;
6
8
var cloneSpliceParentOntoNodeWhen = require ( './clone-splice-parent-onto-node-when' ) ;
7
9
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 ) ] + ) / ) ;
14
12
15
13
function toString ( value ) {
16
14
return String ( value ) ;
@@ -32,37 +30,42 @@ var resolveValue = function(decl, map, /*optional*/ignorePseudoScope, /*internal
32
30
33
31
var variablesUsedInValueMap = { } ;
34
32
// 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
+ } ) ;
42
36
var variablesUsedInValue = Object . keys ( variablesUsedInValueMap ) ;
43
37
44
38
//console.log(debugIndent, (_debugIsInternal ? '' : 'Try resolving'), generateScopeList(decl.parent, true), `ignorePseudoScope=${ignorePseudoScope}`, '------------------------');
45
39
46
40
// Resolve any var(...) substitutons
47
41
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 ( ) )
48
57
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
55
58
var matchingVarDeclMapItem ;
56
59
( map [ variableName ] || [ ] ) . forEach ( function ( varDeclMapItem ) {
57
60
// Make sure the variable declaration came from the right spot
58
61
// And if the current matching variable is already important, a new one to replace it has to be important
59
62
var isRoot = varDeclMapItem . parent . type === 'root' || varDeclMapItem . parent . selectors [ 0 ] === ':root' ;
60
-
63
+
61
64
var underScope = isNodeUnderScope ( decl . parent , varDeclMapItem . parent ) ;
62
65
var underScsopeIgnorePseudo = isNodeUnderScope ( decl . parent , varDeclMapItem . parent , ignorePseudoScope ) ;
63
-
66
+
64
67
//console.log(debugIndent, 'isNodeUnderScope', underScope, underScsopeIgnorePseudo, generateScopeList(varDeclMapItem.parent, true), varDeclMapItem.decl.value);
65
-
68
+
66
69
if (
67
70
underScsopeIgnorePseudo &&
68
71
// 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
71
74
matchingVarDeclMapItem = varDeclMapItem ;
72
75
}
73
76
} ) ;
74
-
77
+
75
78
// Default to the calculatedInPlaceValue which might be a previous fallback, then try this declarations fallback
76
79
var replaceValue = ( matchingVarDeclMapItem || { } ) . calculatedInPlaceValue || ( function ( ) {
77
80
// Resolve `var` values in fallback
@@ -80,32 +83,33 @@ var resolveValue = function(decl, map, /*optional*/ignorePseudoScope, /*internal
80
83
var fallbackDecl = decl . clone ( { parent : decl . parent , value : fallback } ) ;
81
84
fallbackValue = resolveValue ( fallbackDecl , map , false , /*internal*/ true ) . value ;
82
85
}
83
-
86
+
84
87
return fallbackValue ;
85
88
} ) ( ) ;
86
89
// Otherwise if the dependency health is good(no circular or self references), dive deeper and resolve
87
90
if ( matchingVarDeclMapItem !== undefined && ! gatherVariableDependencies ( variablesUsedInValue , map ) . hasCircularOrSelfReference ) {
88
91
// Splice the declaration parent onto the matching entry
89
-
92
+
90
93
var varDeclScopeList = generateScopeList ( decl . parent . parent , true ) ;
91
94
var innerMostAtRuleSelector = varDeclScopeList [ 0 ] . slice ( - 1 ) [ 0 ] ;
92
95
var nodeToSpliceParentOnto = findNodeAncestorWithSelector ( innerMostAtRuleSelector , matchingVarDeclMapItem . decl . parent ) ;
93
96
// See: `test/fixtures/cascade-with-calc-expression-on-nested-rules`
94
97
var matchingMimicDecl = cloneSpliceParentOntoNodeWhen ( matchingVarDeclMapItem . decl , decl . parent . parent , function ( ancestor ) {
95
98
return ancestor === nodeToSpliceParentOnto ;
96
99
} ) ;
97
-
100
+
98
101
replaceValue = resolveValue ( matchingMimicDecl , map , false , /*internal*/ true ) . value ;
99
102
}
100
-
103
+
101
104
isResultantValueUndefined = replaceValue === undefined ;
102
105
if ( isResultantValueUndefined ) {
103
106
warnings . push ( [ 'variable ' + variableName + ' is undefined and used without a fallback' , { node : decl } ] ) ;
104
107
}
105
-
108
+
109
+ // replace original declaration
110
+ resultantValue = resultantValue . replace ( `var(${ match . join ( ',' ) } )` , replaceValue )
111
+
106
112
//console.log(debugIndent, 'replaceValue', replaceValue);
107
-
108
- return replaceValue ;
109
113
} ) ;
110
114
}
111
115
0 commit comments