Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix algorithm to find balanced var() pairs #112

Merged
merged 3 commits into from
Apr 24, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 41 additions & 2 deletions lib/resolve-value.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,45 @@ function toString(value) {
return String(value);
}

// Check for balanced `var(` and `)` pairs inside `value`, and return the 3 fragments:
// `body` (inside), `pre` (before), `post` (after) of the found wrapper
function balancedVar(value) {
var match = balanced('(', ')', value)
if (match) {
// Check if it was prepended with var
if (/(?:^|[^\w-])var$/.test(match.pre)) {
// Remove the var from the end of pre
return {
pre: match.pre.slice(0, -3),
body: match.body,
post: match.post
}
} else {
// Check inside body
var bodyMatch = balancedVar(match.body)
if (bodyMatch) {
// Reconstruct pre and post
return {
pre: match.pre + '(' + bodyMatch.pre,
body: bodyMatch.body,
post: bodyMatch.post + ')' + match.post
}
} else {
// Check inside post
var postMatch = balancedVar(match.post)
if (postMatch) {
// Reconstruct pre
return {
pre: match.pre + '(' + match.body + ')' + postMatch.pre,
body: postMatch.body,
post: postMatch.post
}
}
}
}
}
}

// Pass in a value string to parse/resolve and a map of available values
// and we can figure out the final value
//
Expand All @@ -34,7 +73,7 @@ var resolveValue = function(decl, map, /*optional*/ignorePseudoScope, /*internal
// Create a temporary variable, storing resultantValue variable value
var remainingVariableValue = resultantValue;
// Use balanced lib to find var() declarations and store variable names
while ((matchingVarDecl = balanced('var(', ')', remainingVariableValue))) {
while ((matchingVarDecl = balancedVar(remainingVariableValue))) {
// Split at the comma to find variable name and fallback value
// There may be other commas in the values so this isn't necessarily just 2 pieces
var variableFallbackSplitPieces = matchingVarDecl.body.split(',');
Expand All @@ -61,7 +100,7 @@ var resolveValue = function(decl, map, /*optional*/ignorePseudoScope, /*internal
// var() = var( <custom-property-name> [, <any-value> ]? )
// matches `name[, fallback]`, captures "name" and "fallback"
// See: http://dev.w3.org/csswg/css-variables/#funcdef-var
while ((matchingVarDecl = balanced('var(', ')', resultantValue))) {
while ((matchingVarDecl = balancedVar(resultantValue))) {
var matchingVarDeclMapItem = undefined;

// Split at the comma to find variable name and fallback value
Expand Down
6 changes: 6 additions & 0 deletions test/fixtures/fallback-with-parenthesis.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
:root {
--box-shadow: 0px 2px 2px 0px #fff;
}
.box {
box-shadow: var(--box-shadow, 0px 2px 8px 0px rgba(0, 0, 0, 0.5));
}
3 changes: 3 additions & 0 deletions test/fixtures/fallback-with-parenthesis.expected.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.box {
box-shadow: 0px 2px 2px 0px #fff;
}
1 change: 1 addition & 0 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ describe('postcss-css-variables', function() {


test('should work with variables declared in root', 'root-variable');
test('should work with variables with parenthesis in fallback', 'fallback-with-parenthesis');

test('should work with locally scoped variable in a non-root rule', 'local-variable-non-root');

Expand Down