Skip to content

Commit dff2e3a

Browse files
Fixed a problem with var() declarations in nested functions
1 parent 6ce15de commit dff2e3a

File tree

3 files changed

+81
-59
lines changed

3 files changed

+81
-59
lines changed

lib/resolve-value.js

Lines changed: 61 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ var cloneSpliceParentOntoNodeWhen = require('./clone-splice-parent-onto-node-whe
1010
// var() = var( <custom-property-name> [, <any-value> ]? )
1111
// matches `name[, fallback]`, captures "name" and "fallback"
1212
// See: http://dev.w3.org/csswg/css-variables/#funcdef-var
13-
//var RE_VAR_FUNC = (/var\(\s*(--[^,\s]+?)(?:\s*,\s*(.+))?\s*\)/);
14-
var RE_VAR_FUNC = (/var\(\s*(--[^,\s]+?)(?:\s*,\s*([^)]+))?\s*\)/);
13+
var RE_VAR_FUNC = (/var\(\s*(--[^,\s]+?)(?:\s*,\s*([^()]+|.+))?\s*\)/);
1514

1615
function toString(value) {
1716
return String(value);
@@ -42,66 +41,69 @@ var resolveValue = function(decl, map, /*optional*/ignorePseudoScope, /*internal
4241

4342
// Resolve any var(...) substitutons
4443
var isResultantValueUndefined = false;
45-
resultantValue = resultantValue.replace(new RegExp(RE_VAR_FUNC.source, 'g'), function(match, variableName, fallback) {
46-
// Loop through the list of declarations for that value and find the one that best matches
47-
// By best match, we mean, the variable actually applies. Criteria:
48-
// - is under the same scope
49-
// - The latest defined `!important` if any
50-
var matchingVarDeclMapItem;
51-
(map[variableName] || []).forEach(function(varDeclMapItem) {
52-
// Make sure the variable declaration came from the right spot
53-
// And if the current matching variable is already important, a new one to replace it has to be important
54-
var isRoot = varDeclMapItem.parent.type === 'root' || varDeclMapItem.parent.selectors[0] === ':root';
5544

56-
var underScope = isNodeUnderScope(decl.parent, varDeclMapItem.parent);
57-
var underScsopeIgnorePseudo = isNodeUnderScope(decl.parent, varDeclMapItem.parent, ignorePseudoScope);
58-
59-
//console.log(debugIndent, 'isNodeUnderScope', underScope, underScsopeIgnorePseudo, generateScopeList(varDeclMapItem.parent, true), varDeclMapItem.decl.value);
60-
61-
if(
62-
underScsopeIgnorePseudo &&
63-
// And if the currently matched declaration is `!important`, it will take another `!important` to override it
64-
(!(matchingVarDeclMapItem || {}).isImportant || varDeclMapItem.isImportant)
65-
) {
66-
matchingVarDeclMapItem = varDeclMapItem;
45+
while (resultantValue.match(new RegExp(RE_VAR_FUNC.source, 'g'))) {
46+
resultantValue = resultantValue.replace(new RegExp(RE_VAR_FUNC.source, 'g'), function(match, variableName, fallback) {
47+
// Loop through the list of declarations for that value and find the one that best matches
48+
// By best match, we mean, the variable actually applies. Criteria:
49+
// - is under the same scope
50+
// - The latest defined `!important` if any
51+
var matchingVarDeclMapItem;
52+
(map[variableName] || []).forEach(function(varDeclMapItem) {
53+
// Make sure the variable declaration came from the right spot
54+
// And if the current matching variable is already important, a new one to replace it has to be important
55+
var isRoot = varDeclMapItem.parent.type === 'root' || varDeclMapItem.parent.selectors[0] === ':root';
56+
57+
var underScope = isNodeUnderScope(decl.parent, varDeclMapItem.parent);
58+
var underScsopeIgnorePseudo = isNodeUnderScope(decl.parent, varDeclMapItem.parent, ignorePseudoScope);
59+
60+
//console.log(debugIndent, 'isNodeUnderScope', underScope, underScsopeIgnorePseudo, generateScopeList(varDeclMapItem.parent, true), varDeclMapItem.decl.value);
61+
62+
if(
63+
underScsopeIgnorePseudo &&
64+
// And if the currently matched declaration is `!important`, it will take another `!important` to override it
65+
(!(matchingVarDeclMapItem || {}).isImportant || varDeclMapItem.isImportant)
66+
) {
67+
matchingVarDeclMapItem = varDeclMapItem;
68+
}
69+
});
70+
71+
// Default to the calculatedInPlaceValue which might be a previous fallback, then try this declarations fallback
72+
var replaceValue = (matchingVarDeclMapItem || {}).calculatedInPlaceValue || (function() {
73+
// Resolve `var` values in fallback
74+
var fallbackValue = fallback;
75+
if(fallback) {
76+
var fallbackDecl = decl.clone({ parent: decl.parent, value: fallback });
77+
fallbackValue = resolveValue(fallbackDecl, map, false, /*internal*/true).value;
78+
}
79+
80+
return fallbackValue;
81+
})();
82+
// Otherwise if the dependency health is good(no circular or self references), dive deeper and resolve
83+
if(matchingVarDeclMapItem !== undefined && !gatherVariableDependencies(variablesUsedInValue, map).hasCircularOrSelfReference) {
84+
// Splice the declaration parent onto the matching entry
85+
86+
var varDeclScopeList = generateScopeList(decl.parent.parent, true);
87+
var innerMostAtRuleSelector = varDeclScopeList[0].slice(-1)[0];
88+
var nodeToSpliceParentOnto = findNodeAncestorWithSelector(innerMostAtRuleSelector, matchingVarDeclMapItem.decl.parent);
89+
// See: `test/fixtures/cascade-with-calc-expression-on-nested-rules`
90+
var matchingMimicDecl = cloneSpliceParentOntoNodeWhen(matchingVarDeclMapItem.decl, decl.parent.parent, function(ancestor) {
91+
return ancestor === nodeToSpliceParentOnto;
92+
});
93+
94+
replaceValue = resolveValue(matchingMimicDecl, map, false, /*internal*/true).value;
6795
}
68-
});
69-
70-
// Default to the calculatedInPlaceValue which might be a previous fallback, then try this declarations fallback
71-
var replaceValue = (matchingVarDeclMapItem || {}).calculatedInPlaceValue || (function() {
72-
// Resolve `var` values in fallback
73-
var fallbackValue = fallback;
74-
if(fallback) {
75-
var fallbackDecl = decl.clone({ parent: decl.parent, value: fallback });
76-
fallbackValue = resolveValue(fallbackDecl, map, false, /*internal*/true).value;
96+
97+
isResultantValueUndefined = replaceValue === undefined;
98+
if(isResultantValueUndefined) {
99+
warnings.push(['variable ' + variableName + ' is undefined and used without a fallback', { node: decl }]);
77100
}
78-
79-
return fallbackValue;
80-
})();
81-
// Otherwise if the dependency health is good(no circular or self references), dive deeper and resolve
82-
if(matchingVarDeclMapItem !== undefined && !gatherVariableDependencies(variablesUsedInValue, map).hasCircularOrSelfReference) {
83-
// Splice the declaration parent onto the matching entry
84-
85-
var varDeclScopeList = generateScopeList(decl.parent.parent, true);
86-
var innerMostAtRuleSelector = varDeclScopeList[0].slice(-1)[0];
87-
var nodeToSpliceParentOnto = findNodeAncestorWithSelector(innerMostAtRuleSelector, matchingVarDeclMapItem.decl.parent);
88-
// See: `test/fixtures/cascade-with-calc-expression-on-nested-rules`
89-
var matchingMimicDecl = cloneSpliceParentOntoNodeWhen(matchingVarDeclMapItem.decl, decl.parent.parent, function(ancestor) {
90-
return ancestor === nodeToSpliceParentOnto;
91-
});
92-
93-
replaceValue = resolveValue(matchingMimicDecl, map, false, /*internal*/true).value;
94-
}
95-
96-
isResultantValueUndefined = replaceValue === undefined;
97-
if(isResultantValueUndefined) {
98-
warnings.push(['variable ' + variableName + ' is undefined and used without a fallback', { node: decl }]);
99-
}
100-
101-
//console.log(debugIndent, 'replaceValue', replaceValue);
102-
103-
return replaceValue;
104-
});
101+
102+
//console.log(debugIndent, 'replaceValue', replaceValue);
103+
104+
return replaceValue;
105+
});
106+
}
105107

106108
return {
107109
// The resolved value
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
:root {
2+
--some-width: 150px;
3+
}
4+
15
.box-foo {
26
background-color: color(var(--some-color, white));
37
}
8+
9+
.box-foo {
10+
width: calc(58.3333333333% - var(--some-width, 100px));
11+
}
12+
13+
.box-foo {
14+
width: calc(58.3333333333% - var(--missing, 100px));
15+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
11
.box-foo {
22
background-color: color(white);
33
}
4+
5+
.box-foo {
6+
width: calc(58.3333333333% - 150px);
7+
}
8+
9+
.box-foo {
10+
width: calc(58.3333333333% - 100px);
11+
}

0 commit comments

Comments
 (0)