I'm trying to write a LESS mixin that uses part of a string to form a class name. I expected to be able to use replace()
to provide a value for a variable and then use the variable in the rule. Here's the test I wrote to verify that it would work:
.foo(@xyz) {
@pfx: replace(@xyz, "(.).*", "$1");
.@{pfx}-foo { margin: 0; }
}
.foo(abcdefghijklmnop);
When I run that through lessc (1.7.4) (edit - also 2.1.0), it kind-of works, generating this:
.aaa-foo {
margin: 0;
}
So, the replace()
correctly plucks the first character ("a") from the string I pass in, but instead of
.a-foo
it gives me
.aaa-foo
I've looked at the source for the replace()
function and it's dirt-simple, so I'm just completely confused about what's going on. (I've tried the regex as "^(.).*$"
with the same result.)
I extended the test a little:
.foo(@xyz) {
@pfx: replace(@xyz, "(.).*", "$1");
.@{pfx}-foo { margin: 0; content: "@{pfx}"; }
}
.foo(abcdefghijklmnop);
which gives me:
.aaa-foo {
margin: 0;
content: "a";
}
which means that the regex is working fine.
Try this:
@pfx: e(replace(@xyz, "^(.).*$", "$1"));
Or this:
@pfx: replace(~"@{xyz}", "^(.).*$", "$1");
See documentation for the e function.
Apparently a few workarounds are provided, but how it works still a myth.
The AST after evaluation was totally correct, which looks like:
Element { combinator: { value: '', emptyOrWhitespace: true },
value: '.',
index: 54,
currentFileInfo:
{ filename: 'input',
relativeUrls: undefined,
rootpath: '',
currentDirectory: '',
entryPath: '',
rootFilename: 'input' } },
Element { combinator: { value: '', emptyOrWhitespace: true },
value:
Quoted { escaped: undefined,
value: 'a',
quote: 'a',
index: undefined,
currentFileInfo: undefined },
index: 55,
currentFileInfo:
{ filename: 'input',
relativeUrls: undefined,
rootpath: '',
currentDirectory: '',
entryPath: '',
rootFilename: 'input' } },
Element { combinator: { value: '', emptyOrWhitespace: true },
value: '-foo',
index: 61,
currentFileInfo:
{ filename: 'input',
relativeUrls: undefined,
rootpath: '',
currentDirectory: '',
entryPath: '',
rootFilename: 'input' } }
But when generating the CSS code, the things go south quickly.
Quoted.prototype.genCSS = function (context, output) {
if (!this.escaped) {
output.add(this.quote, this.currentFileInfo, this.index);
}
output.add(this.value);
if (!this.escaped) {
output.add(this.quote);
}
};
Quoted
node will output the value with quote around it when it is not escaped. Whereas, somehow, the quote is the first char of the value, say 'a'
in this case.
And when apply e()
around replace
, less will put Anonymous
node in the AST instead of Quoted
node, thus no quote will be outputted.
try this
.foo(@xyz) {
@props: ~`"@{arguments}"`;
@pfx: replace(@props, "^(.).*$", "$1");
.@{pfx}-foo { margin: 0; }
}
.foo(abcdefghijklmnop);
EDIT
Also answering the comment, you want to pass a string to replace, actually a simpler version would be to use .foo(~"abcdefghijklmnop")
that forces less to consider the parameter as string without outputting the quotes.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With