-
Notifications
You must be signed in to change notification settings - Fork 13
[FIX] CSS Variables: Fix variable handling / relative urls #172
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
Changes from 5 commits
c8f3595
77ca02c
6217f5a
1fbb1a6
b2ec78d
64c00e9
c99433f
1f41cc1
9e52bc9
6169b39
76edeed
15c6fb8
a24aa11
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,12 +11,13 @@ const CSSVariablesCollectorPlugin = module.exports = function(config) { | |
this.ruleStack = []; | ||
this.mixinStack = []; | ||
this.parenStack = []; | ||
this.importStack = []; | ||
}; | ||
|
||
CSSVariablesCollectorPlugin.prototype = { | ||
|
||
// needed to keep the less variable references intact to use this info for the CSS variables references | ||
isPreEvalVisitor: true, | ||
|
||
isReplacing: true, | ||
|
||
_isInMixinOrParen() { | ||
|
@@ -27,27 +28,41 @@ CSSVariablesCollectorPlugin.prototype = { | |
return this.ruleStack.length > 0 && !this.ruleStack[this.ruleStack.length - 1].variable; | ||
}, | ||
|
||
_isInGlobalOrBaseImport() { | ||
return this.config.libName !== "sap.ui.core" && this.importStack.filter((importNode) => { | ||
return /\/(?:global|base)\.less$/.test(importNode.importedFilename); | ||
}).length > 0; | ||
_isVarInLibrary({varName, filename}) { | ||
// for libraries we check that the file is within the libraries theme path | ||
// in all other cases with no filename (indicates calculated variables) | ||
// or in case of variables in standalone less files we just include them! | ||
const include = !filename || | ||
(this.config.libPath ? filename.startsWith(`/resources/${this.config.libPath}/themes/`) : true); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can't rely on the fact that the path starts with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, so more my initial regex approach again! |
||
return include; | ||
}, | ||
|
||
_isRelevant() { | ||
return !this._isInMixinOrParen() && this._isVarInRule(); | ||
}, | ||
|
||
toLessVariables() { | ||
toLessVariables(varsOverride) { | ||
const vars = {}; | ||
Object.keys(this.vars).forEach((key) => { | ||
const override = this.vars[key].updateAfterEval && varsOverride[key] !== undefined; | ||
if (override && process && process.env && process.env.LESS_OPENUI5_CSSVARS_REPORT_OVERRIDE) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. console log only with ENV variable set (to be on the safe side for eventual usage in browser checking process.env to be available first) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For that I think you would need to to a |
||
console.log(`Override variable "${key}" from "${this.vars[key].css}" to "${varsOverride[key].css}"`); | ||
} | ||
vars[key] = { | ||
css: override ? varsOverride[key].css : this.vars[key].css, | ||
export: this.vars[key].export | ||
}; | ||
}); | ||
let lessVariables = ""; | ||
Object.keys(this.vars).forEach((value, index) => { | ||
lessVariables += "@" + value + ": " + this.vars[value].css + ";\n"; | ||
Object.keys(vars).forEach((value, index) => { | ||
lessVariables += "@" + value + ": " + vars[value].css + ";\n"; | ||
}); | ||
Object.keys(this.calcVars).forEach((value, index) => { | ||
lessVariables += "@" + value + ": " + this.calcVars[value].css + ";\n"; | ||
}); | ||
lessVariables += "\n:root {\n"; | ||
Object.keys(this.vars).forEach((value, index) => { | ||
if (this.vars[value].export) { | ||
Object.keys(vars).forEach((value, index) => { | ||
if (vars[value].export) { | ||
lessVariables += "--" + value + ": @" + value + ";\n"; | ||
} | ||
}); | ||
|
@@ -95,7 +110,7 @@ CSSVariablesCollectorPlugin.prototype = { | |
}, | ||
|
||
visitCall(node, visitArgs) { | ||
// if variables are used inside rules, generate a new dynamic variable for it! | ||
// if variables are used inside rules, generate a new calculated variable for it! | ||
const isRelevantFunction = typeof less.tree.functions[node.name] === "function" && ["rgba"].indexOf(node.name) === -1; | ||
if (this._isRelevant() && isRelevantFunction) { | ||
const css = this._getCSS(node); | ||
|
@@ -109,7 +124,9 @@ CSSVariablesCollectorPlugin.prototype = { | |
} | ||
this.calcVars[newName] = { | ||
css: css, | ||
export: !this._isInGlobalOrBaseImport() | ||
export: this._isVarInLibrary({ | ||
varName: newName | ||
}) | ||
}; | ||
return new less.tree.Call("var", [new less.tree.Anonymous("--" + newName, node.index, node.currentFileInfo, node.mapLines)]); | ||
} | ||
|
@@ -133,6 +150,20 @@ CSSVariablesCollectorPlugin.prototype = { | |
}, | ||
|
||
visitRule(node, visitArgs) { | ||
// check rule for being a variable declaration | ||
const isVarDeclaration = typeof node.name === "string" && node.name.startsWith("@"); | ||
if (!this._isInMixinOrParen() && isVarDeclaration) { | ||
// add the variable declaration to the list of vars | ||
const varName = node.name.substr(1); | ||
const isVarInLib = this._isVarInLibrary({ | ||
varName, | ||
filename: node.currentFileInfo.filename | ||
}); | ||
this.vars[varName] = { | ||
css: this._getCSS(node.value), | ||
export: isVarInLib | ||
}; | ||
} | ||
// store the rule context for the call variable extraction | ||
this.ruleStack.push(node); | ||
return node; | ||
|
@@ -168,29 +199,13 @@ CSSVariablesCollectorPlugin.prototype = { | |
return node; | ||
}, | ||
|
||
visitImport(node, visitArgs) { | ||
// store the import context | ||
this.importStack.push(node); | ||
return node; | ||
}, | ||
|
||
visitImportOut(node) { | ||
// remove import context | ||
this.importStack.pop(); | ||
return node; | ||
}, | ||
|
||
visitRuleset(node, visitArgs) { | ||
node.rules.forEach((value) => { | ||
const isVarDeclaration = value instanceof less.tree.Rule && typeof value.name === "string" && value.name.startsWith("@"); | ||
if (!this._isInMixinOrParen() && isVarDeclaration) { | ||
// add the variable declaration to the list of vars | ||
this.vars[value.name.substr(1)] = { | ||
css: this._getCSS(value.value), | ||
export: !this._isInGlobalOrBaseImport() | ||
}; | ||
} | ||
}); | ||
visitUrl(node, visitArgs) { | ||
// we mark the less variables which should be updated after eval | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My expectation was actually the other way round. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are issues within the isPreEval phase where less variables with the value none also have the URL content - so doing it this way is the best to determine properly the variables and apply only the needed later! All other options require much more code! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Got it. Would be great to have those specialities documented somewhere. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is described in my comments |
||
// => strangewise less variables with "none" values are also urls | ||
// after the less variables have been evaluated | ||
if (this.ruleStack.length > 0 && this.ruleStack[0].variable) { | ||
this.vars[this.ruleStack[0].name.substr(1)].updateAfterEval = true; | ||
} | ||
return node; | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
@@ -0,0 +1,48 @@ | ||||
"use strict"; | ||||
|
||||
const less = require("../thirdparty/less"); | ||||
|
||||
const CSSVariablesUrlResolverPlugin = module.exports = function(config) { | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need another plugin? Can't we just re-use what the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because of running it at a later point of time! isPreEvalVisitor vs. isPreVisitor - otherwise we have to write ugly if conditions in the coding There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you give me some more details here? VariableCollector also uses There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The logic for the detection of the URLs is pretty simple and it need to run in the PreVisitor phase. The CSS Var Collector is running in the PreEvalVisitor phase to not resolve the vars. I don't want to run the full enchilada of the code of the CSS Var Collector again in the PreVisitor phase as this is unnecessary and IMO the code of the URL Collector is pretty trivial. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ahh - got your point - I am checking the url-collector right now There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's no need to run anything again. Line 237 in a6fea7c
It's a different There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. url-collector is not what I meant, and I think it doesn't really fit for this purpose. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||
this.config = config; | ||||
// eslint-disable-next-line new-cap | ||||
this.native = new less.tree.visitor(this); | ||||
this.vars = {}; | ||||
this.ruleStack = []; | ||||
}; | ||||
|
||||
CSSVariablesUrlResolverPlugin.prototype = { | ||||
|
||||
isPreVisitor: true, | ||||
|
||||
run(root) { | ||||
return this.native.visit(root); | ||||
}, | ||||
|
||||
getVariables() { | ||||
return this.vars; | ||||
}, | ||||
|
||||
visitRule(node, visitArgs) { | ||||
// store the rule context for the call variable extraction | ||||
this.ruleStack.push(node); | ||||
return node; | ||||
}, | ||||
|
||||
visitRuleOut(node) { | ||||
// remove rule context | ||||
this.ruleStack.pop(); | ||||
return node; | ||||
}, | ||||
|
||||
visitUrl(node, visitArgs) { | ||||
// for top-level variables we need the resolved urls | ||||
if (this.ruleStack.length > 0 && this.ruleStack[0].variable) { | ||||
const varName = this.ruleStack[0].name.substr(1); | ||||
this.vars[varName] = { | ||||
css: node.toCSS() | ||||
}; | ||||
} | ||||
return node; | ||||
} | ||||
|
||||
}; |
Uh oh!
There was an error while loading. Please reload this page.