Closed arvinxx closed 2 years ago
I want to make a less plugin to make less variable dynamic with css variable, since less variable can't use in browser runtime.
Side note: being able to have variables be dynamic at runtime is one reason I'm building Jess.
In terms of visitors and the Less AST, IIRC, returning an array should be possible, but the safest thing to do when visiting a node is returning a node. Note that you need to have isReplacing
set to true
if your visitor is replacing nodes (and then all visited rules need to return the node it entered with or a new node).
Another trick you can use instead of returning an array is returning a Ruleset with the selector as a single &
element. That should collapse? As long as your visitor is a pre-eval visitor? On evaluation (and later rule visiting), arrays and rulesets should be merged.
Does that help? Did you have both isPreEvalVisitor
and isReplacing
set to true
?
Did you have both isPreEvalVisitor and isReplacing set to true?
Yeah ,I have set both isPreEvalVisitor
and isReplacing
to true
.
The question is I don't know the struct syntax of a Node. For example
returning a Ruleset with the selector as a single
&
element
I'm just wondering the right way to do this. Is the below a right structure?
new Ruleset(
[new Selector(new Element('&'))],
[
node, // the input node
new Ruleset(
[new Selector([new Element(new Combinator(' '), ':root')], [])],
[declaration], // new
),
],
)
or just use an element node ?
new Ruleset(
new Element('&'),
[
node, // the input node
new Ruleset(
[new Selector([new Element(new Combinator(' '), ':root')], [])],
[declaration], // new
),
],
)
I try in both way, but all failed with error below. Here are these codes
Error: error evaluating function `multiplyTwo`: variable @base-number is undefined
If you want to try, just clone and install, then run npm run jest
or yarn jest
, the failed test is in test/e2e.test.ts
named transform less variable to css variable with function'
I think it may be the relpace rule doesn't work right.
It's a huge need to know intermediate state of less code. for example, I really need to know whether@base-number: 10;
is transformed to
@base-number: 10;
:root {
--base-number: @base-number;
}
However there seems to be no way to check intermediate code (or just I don't know)
That's a really pain in ast development😰
Side note: being able to have variables be dynamic at runtime is one reason I'm building Jess.
That seems interesting, look forward to it :)
I don't understand why you don't just do this:
.mixin(@base-number) {
@base: @base-number;
@multiply-number: multiplyTwo(@base);
--base-number: @{base};
--multiply-number: @{multiply-number};
}
:root {
.mixin(10);
}
:root[scope='local'] {
.mixin(2);
}
.use {
color: var(--base-number);
multiply: var(--multiply-number);
}
Note: looks like there might be a bug in evaluating variables in custom properties in mixin scope, but by aliasing @base-number
to @base
it seems to work.
@arvinxx
Error: error evaluating function
multiplyTwo
: variable @base-number is undefined
I wonder if you're encountering the same bug as above with trying to evaluate @base-number
in a custom property? It's possible you're doing everything else correctly.
@matthew-dean
The reason i don't use mixin is that I need this plugin to be compatible with existed codebase ( for example antd
).
I have try a visitor:
visitDeclaration(node) {
if (!(typeof node.name === 'string') || !node.name.match(/^@/)) {
return node;
}
const declaration = new this.tree.Declaration(
node.name.replace(/^@/, '--'),
node.value,
);
if (!node.parent) return declaration;
if (node.parent.root) {
const { Ruleset, Selector, Element } = this.tree;
return new Ruleset(
new Element('&'),
[
node,
new Ruleset([new Selector([new Element(':root')])], [declaration]),
],
);
}
return declaration;
}
and use the test case below:
@bg: #000;
@fg: #fff;
@media (print) {
@bg: #fff; // Override
@fg: #000;
}
I expect to get result of
:root {
--bg: #000;
}
:root {
--fg: #fff;
}
@media (print) {
:root {
--bg: #fff;
}
:root {
--fg: #000;
}
}
but the result I get finally is below
@media (print) {
--bg: #fff;
--fg: #000;
}
Is there something wrong with the previous visitor function?
@arvinxx .parent
is not a reliable property; don't use it. If you need the root, store a reference to it when it's first visited (the first visitRuleset
, I believe).
There is a much issue problem about this issue -> #3600 .
TL;DR
I want to make a less plugin to make less variable dynamic with css variable, since less variable can't use in browser runtime. But I meet some difficulties when deal with less AST.
There are:
Declaration
node by an array ofDeclaration
node and aRuleset
nodeVariable
node;Target
less is a comilping-runtime language,so less variables don't exist on browser runtime. My idea is to change less variables to css variable when comilping with right order(So it can be compatible with less function). Then we can use css variable in browser to have a dynamic result.
a simple example is below:
input:
outputs:
With this idea, I try to work on the less-plugin-dynamic-variable to implement it.
Steps to achieve this example
Add target css variable
after configuartion, I want to add the target css variable with less variable.So when in comile
variables with less funtion:
I have referred to less-plugin-custom-properties about Declaration node, but I failed. Because I don't know the right way to replace a node by array. My code is below:
And it seems there are little information about less AST structure. (Really struggle with it 😞)
handle less variable in usage
It seems much easier below:
I need to know which properties to replace, but
visitVariable
don't has context about it. So I just fail againHow can I deal with it?