Skip to content

Commit 77ca02c

Browse files
committed
[FIX] Variables: resolve url values in variables
1 parent c8f3595 commit 77ca02c

File tree

3 files changed

+98
-29
lines changed

3 files changed

+98
-29
lines changed

lib/index.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -288,10 +288,13 @@ Builder.prototype.build = function(options) {
288288
// generate the skeleton-css and the less-variables
289289
const CSSVariablesCollectorPlugin = require("./plugin/css-variables-collector");
290290
const oCSSVariablesCollector = new CSSVariablesCollectorPlugin(config);
291+
const CSSVariablesUrlResolverPlugin = require("./plugin/css-variables-urlresolver");
292+
const oCSSVariablesUrlResolverPlugin = new CSSVariablesUrlResolverPlugin(config);
291293
result.cssSkeleton = tree.toCSS(Object.assign({}, options.compiler, {
292-
plugins: [oCSSVariablesCollector]
294+
plugins: [oCSSVariablesCollector, oCSSVariablesUrlResolverPlugin]
293295
}));
294-
result.cssVariablesSource = oCSSVariablesCollector.toLessVariables();
296+
const varsOverride = oCSSVariablesUrlResolverPlugin.getVariables();
297+
result.cssVariablesSource = oCSSVariablesCollector.toLessVariables(varsOverride);
295298
if (oRTL) {
296299
const oCSSVariablesCollectorRTL = new CSSVariablesCollectorPlugin(config);
297300
result.cssSkeletonRtl = tree.toCSS(Object.assign({}, options.compiler, {

lib/plugin/css-variables-collector.js

Lines changed: 45 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,13 @@ const CSSVariablesCollectorPlugin = module.exports = function(config) {
1111
this.ruleStack = [];
1212
this.mixinStack = [];
1313
this.parenStack = [];
14-
this.importStack = [];
1514
};
1615

1716
CSSVariablesCollectorPlugin.prototype = {
1817

18+
// needed to keep the less variable references intact to use this info for the CSS variables references
1919
isPreEvalVisitor: true,
20+
2021
isReplacing: true,
2122

2223
_isInMixinOrParen() {
@@ -27,28 +28,41 @@ CSSVariablesCollectorPlugin.prototype = {
2728
return this.ruleStack.length > 0 && !this.ruleStack[this.ruleStack.length - 1].variable;
2829
},
2930

30-
_isVarInLibrary() {
31-
// the last less file defines the location of the css variable and this must
32-
// be in the path of the current library, otherwise it is an external variable
33-
const regexp = new RegExp(`^/resources/${this.config.libPath}/themes/`);
34-
return this.importStack.length > 0 ? regexp.test(this.importStack[this.importStack.length - 1].importedFilename) : true;
31+
_isVarInLibrary({varName, filename}) {
32+
// for libraries we check that the file is within the libraries theme path
33+
// in all other cases with no filename (indicates calculated variables)
34+
// or in case of variables in standalone less files we just include them!
35+
const include = !filename ||
36+
(this.config.libPath ? filename.startsWith(`/resources/${this.config.libPath}/themes/`) : true);
37+
return include;
3538
},
3639

3740
_isRelevant() {
3841
return !this._isInMixinOrParen() && this._isVarInRule();
3942
},
4043

41-
toLessVariables() {
44+
toLessVariables(varsOverride) {
45+
const vars = {};
46+
Object.keys(this.vars).forEach((key) => {
47+
const override = this.vars[key].updateAfterEval && varsOverride[key] !== undefined;
48+
if (override) {
49+
console.log(`Override variable "${key}" from "${this.vars[key].css}" to "${varsOverride[key].css}"`);
50+
}
51+
vars[key] = {
52+
css: override ? varsOverride[key].css : this.vars[key].css,
53+
export: this.vars[key].export
54+
};
55+
});
4256
let lessVariables = "";
43-
Object.keys(this.vars).forEach((value, index) => {
44-
lessVariables += "@" + value + ": " + this.vars[value].css + ";\n";
57+
Object.keys(vars).forEach((value, index) => {
58+
lessVariables += "@" + value + ": " + vars[value].css + ";\n";
4559
});
4660
Object.keys(this.calcVars).forEach((value, index) => {
4761
lessVariables += "@" + value + ": " + this.calcVars[value].css + ";\n";
4862
});
4963
lessVariables += "\n:root {\n";
50-
Object.keys(this.vars).forEach((value, index) => {
51-
if (this.vars[value].export) {
64+
Object.keys(vars).forEach((value, index) => {
65+
if (vars[value].export) {
5266
lessVariables += "--" + value + ": @" + value + ";\n";
5367
}
5468
});
@@ -96,7 +110,7 @@ CSSVariablesCollectorPlugin.prototype = {
96110
},
97111

98112
visitCall(node, visitArgs) {
99-
// if variables are used inside rules, generate a new dynamic variable for it!
113+
// if variables are used inside rules, generate a new calculated variable for it!
100114
const isRelevantFunction = typeof less.tree.functions[node.name] === "function" && ["rgba"].indexOf(node.name) === -1;
101115
if (this._isRelevant() && isRelevantFunction) {
102116
const css = this._getCSS(node);
@@ -110,7 +124,9 @@ CSSVariablesCollectorPlugin.prototype = {
110124
}
111125
this.calcVars[newName] = {
112126
css: css,
113-
export: this._isVarInLibrary()
127+
export: this._isVarInLibrary({
128+
varName: newName
129+
})
114130
};
115131
return new less.tree.Call("var", [new less.tree.Anonymous("--" + newName, node.index, node.currentFileInfo, node.mapLines)]);
116132
}
@@ -169,30 +185,32 @@ CSSVariablesCollectorPlugin.prototype = {
169185
return node;
170186
},
171187

172-
visitImport(node, visitArgs) {
173-
// store the import context
174-
this.importStack.push(node);
175-
return node;
176-
},
177-
178-
visitImportOut(node) {
179-
// remove import context
180-
this.importStack.pop();
181-
return node;
182-
},
183-
184188
visitRuleset(node, visitArgs) {
185189
node.rules.forEach((value) => {
186190
const isVarDeclaration = value instanceof less.tree.Rule && typeof value.name === "string" && value.name.startsWith("@");
187191
if (!this._isInMixinOrParen() && isVarDeclaration) {
188192
// add the variable declaration to the list of vars
189-
this.vars[value.name.substr(1)] = {
193+
const varName = value.name.substr(1);
194+
this.vars[varName] = {
190195
css: this._getCSS(value.value),
191-
export: this._isVarInLibrary()
196+
export: this._isVarInLibrary({
197+
varName,
198+
filename: value.currentFileInfo.filename
199+
})
192200
};
193201
}
194202
});
195203
return node;
204+
},
205+
206+
visitUrl(node, visitArgs) {
207+
// we mark the less variables which should be updated after eval
208+
// => strangewise less variables with "none" values are also urls
209+
// after the less variables have been evaluated
210+
if (this.ruleStack.length > 0 && this.ruleStack[0].variable) {
211+
this.vars[this.ruleStack[0].name.substr(1)].updateAfterEval = true;
212+
}
213+
return node;
196214
}
197215

198216
};
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
"use strict";
2+
3+
const less = require("../thirdparty/less");
4+
5+
const CSSVariablesUrlResolverPlugin = module.exports = function(config) {
6+
this.config = config;
7+
// eslint-disable-next-line new-cap
8+
this.native = new less.tree.visitor(this);
9+
this.vars = {};
10+
this.ruleStack = [];
11+
};
12+
13+
CSSVariablesUrlResolverPlugin.prototype = {
14+
15+
isPreVisitor: true,
16+
17+
run(root) {
18+
return this.native.visit(root);
19+
},
20+
21+
getVariables() {
22+
return this.vars;
23+
},
24+
25+
visitRule(node, visitArgs) {
26+
// store the rule context for the call variable extraction
27+
this.ruleStack.push(node);
28+
return node;
29+
},
30+
31+
visitRuleOut(node) {
32+
// remove rule context
33+
this.ruleStack.pop();
34+
return node;
35+
},
36+
37+
visitUrl(node, visitArgs) {
38+
// for top-level variables we need the resolved urls
39+
if (this.ruleStack.length > 0 && this.ruleStack[0].variable) {
40+
const varName = this.ruleStack[0].name.substr(1);
41+
this.vars[varName] = {
42+
css: node.toCSS()
43+
};
44+
}
45+
return node;
46+
}
47+
48+
};

0 commit comments

Comments
 (0)