2

I am finding that LESS has a hobbled JavaScript evaluator, at least the way I am using it, which is to compile *.less files into *.css on a client before uploading them to the web server.

I'm aware that compilation may be more often done server-side, but for performance & simplicity we only want CSS files on the server. I'm compiling the LESS files on Fedora Linux with the lessc ruby gem installed into Node Package Manager as in these instructions.

The compiler is working perfectly, but as far as I can tell the JavaScript expression evaluation is sorely limited. I believe this also applies to server-side JavaScript expression evaluation based on this posting which suggests uncertainty about how the JavaScript engine is plugged into the LESS environment.

All I can use are simple, comma-separated expressions, like the following:

@bar: `
"ignored-string-expression"
,
5
`;
div.test-thing { content: ~"@{bar}"; }

which compiles into:

div.test-thing {
  content: 5;
}

When I try to define a function, the compiler barfs (whether or not the semicolon in the expression is backslash-escaped):

[719] anjaneya% cat testfunc.less
@bar: `function foo() {return 5}; foo()`;
div.test-thing { content: ~"@{bar}"; }

[720] anjaneya% lessc testfunc.less
SyntaxError: JavaScript evaluation error: `function foo() {return 5}; foo()` ...

There also doesn't seem to be any way of looping, even if you try to trick it into evaluating a loop as you would the "ignored-string-expression" above, like:

@foo: `x = 0,
for (var n = 0; n <= 10; n++) { x++; },
x
`;
div.test-thing { content: ~"@{bar}"; }

which says:

ParseError: Syntax Error on line 1 ...

Why bother? To be able to compile this LESS:

@svgSource: '<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%"><linearGradient id="g" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="@{start}" /><stop offset="1" stop-color="@{end}" /></linearGradient><rect x="0" y="0" width="100%" height="100%" fill="url(#g)" /></svg>';

into this CSS:

background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiPjxsaW5lYXJHcmFkaWVudCBpZD0iZyIgeDE9IjAiIHkxPSIwIiB4Mj0iMCIgeTI9IjEiPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iIzU3OWRkYiIgLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiMwMDAwMjIiIC8+PC9saW5lYXJHcmFkaWVudD48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2cpIiAvPjwvc3ZnPg==);

using a program like this, whether the algorithm is implemented in JavaScript, PHP, Perl, UNIX shell, or anything else. This processing might be done without function definitions but not without looping, and without functions you can't even have recursion.

Given that both functions and looping are compound statements that probably aren't evaluated as expressions (it's not LISP), that is probably the basis for the failure... it's not really a full JavaScript interpreter. So I'm hoping someone who really knows the LESS compiler will:

  • clarify the limitation above, so I can use JavaScript & LESS portably for this task
  • say how this problem can be worked around (e.g., with a "shell escape" or any evaluation environment that can process strings iteratively) and/or
  • say how to extend the LESS compiler with such text processing capabilities, like a "real" JavaScript engine.
4

3 に答える 3

2

The data-uri() function is now built into LESS:

http://lesscss.org/functions/#misc-functions-data-uri

Will that help?

于 2014-05-23T12:33:17.917 に答える
1

I think what you're looking for is both a mixture of a BUNCH of little frustrating things about using javascript in LESS.

  1. All javascript has to be on one line. (I... don't even.. whatever.)
  2. All javascript has to return something or you get the completely useless error "Cannot read property 'value' of undefined"
  3. All javascript must be one statement. This comes from the original use case where they were thinking you would do something like @foo: ~'something.javascript.is.good.at({@bar});'

Number 1 is formatting, 2 just means you need to return something even if you don't use it, but number 3 catches a lot of people off guard. To get around it, just make you "one thing" a self-executing anonymous function.

Eg:

(function(){
    do.some.cool.stuff(); 
    other.cool.things();
    return('Fry');
})();

So the LESS parser sees that (make sure it's all in one line instead of how I wrote it!), bundles it off to javascript land as a single execution, reads the return value, and calls it a day.

If you want to see it in action, I recently wrote LESS mixin to make RGBA 1x1 pixels for background fills, etc. that uses all of this madness.

Hope that helps!

于 2014-01-31T21:51:00.483 に答える
-1

I know this post is a couple of years old but javascript embedded in LESS can be very handy so I though I would post some tips:

 //All javascript must 
 // a. be on the rhs of an assignment   e.g.  @x:` var x = @{val}+7`;
 // b. must evaluate to a value
 // c. cannot be terminated by a ';'
 //
 // To get around this, multiline code can be packed into an IIFE......

 @val:7;

 @loop:`(function(val){
   var sum=0;
   for(var i=0; i<val; i++){sum+=i;}
   return sum;
 } )(@{val})`;

 .test{
   content:@loop;  // content: 21;
 }

 // console.log writes directly to the beginning of the output file
 @x:`console.log('/*...... loop = @{loop}.......*/')`;  //   /*...... loop =    21.......*/

 // you can use the global variable.  Here we attach a library module to it.....
 @x:`global.myLib={}`;

 // Then add a method to the module..............
 @btoa:`myLib.btoa=function(str){
   var buffer = new Buffer((str).toString(), 'binary');
   return buffer.toString('base64');
 }`;

 // And invoke the method to encode some text............................
 @sometext:'LESS is more (more or less)';

 .test2{
    content:`myLib.btoa(@{sometext})`;// content: "TEVTUyBpcyBtb3JlIChtb3JlIG9yIGxlc3Mp";
 }
于 2016-12-26T00:36:05.923 に答える