less.js icon indicating copy to clipboard operation
less.js copied to clipboard

Escape function with nested var failed

Open Corcules opened this issue 6 years ago • 10 comments

Version

Less 3.8.1 via CodeKit

description

An error is thrown when escaping color variable and using it in an other var as string. No error with Less 2.x

A less var, svg image for data-uri, with a nested less var "@color-icone" for fill color

@icone-arrow-next : "data:image/svg+xml;utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 49 37'><path fill='@{color-icone}' d='M31.1 0l-1.5 1.5 15.5 16H0v2h45.1l-15.5 16 1.5 1.5L49 18.5z'/></svg>";

Using the var elsewhere

#selector { @color-icone : escape(#ffffff); background-image : url( @icone-arrow-next ) ; }

The error come with the escape function which transform the color code '#ffffff' to '%23ffffff'. No error without the escape function but the color string is not encoded...

error message

SyntaxError: Invalid % without number in /Users/path/to/file

Corcules avatar Oct 02 '18 12:10 Corcules

Have you tried first converting to a string? The escape function is documented as only accepting strings as its parameter, after all.

You can perform the conversion inline with the escape operation by using the % format function:

@color-icone : escape( %( "%s", #ffffff ));

rjgotten avatar Oct 04 '18 09:10 rjgotten

Sorry, just try but not working with inline string conversion. exactly the same error.

Corcules avatar Oct 08 '18 13:10 Corcules

Fews more tests :

@color-icone : escape( #ffffff ); @color-icone : escape( %( "%s", #ffffff ));

Both are working with Less 2.7.1.

Both not working with Less 3.8.1

Corcules avatar Oct 08 '18 13:10 Corcules

The % function atleast should call toCSS for every node type which is not Quoted, i.e. a quoted string value:

https://github.com/less/less.js/blob/a84358d2956d3245f45083adc50069119de2e431/lib/less/functions/string.js#L22-L36

However, it calls toCSS without arguments and it looks like the Color node type atleast expects some parameters there:

https://github.com/less/less.js/blob/a84358d2956d3245f45083adc50069119de2e431/lib/less/tree/color.js#L70

@matthew-dean I think we have ourselves a bug here, right?

[EDIT] Yup definitely a bug. It looks like the general implementation in tree/node.js expects toCSS to always be called with a context parameter as well.

rjgotten avatar Oct 08 '18 15:10 rjgotten

@rjgotten 👍

matthew-dean avatar Oct 09 '18 17:10 matthew-dean

For svg issue, I've overrided escape function.

  1. Create a plugin (my-plugin.js)
module.exports = {
    install: (less, pluginManager, functions) => {
        functions.add('escape', str => encodeURI(str.value));
    }
};
  1. Import a plugin:
@plugin './my-plugin';

Original escape function v3.9.0

encodeURI(str.value).replace(/=/g, '%3D').replace(/:/g, '%3A').replace(/#/g, '%23').replace(/;/g, '%3B').replace(/\(/g, '%28').replace(/\)/g, '%29'));

For some reason, full replacement was failing, even in the plugin.

easingthemes avatar Aug 11 '19 17:08 easingthemes

SyntaxError: Invalid % without number in /Users/path/to/file

Unfortunately, this error also occurs when it's not dealing with the color. (Example) And also occurs when it's not dealing with the svg string. (Example)

I have tried the @easingtheme 's solution, and it seems working fine.

Update: It seems that escape() is not returning the string.

  1. --var: escape('<svg>'); is working fine, but yields --var: %3Csvg%3E;
  2. saving escape() result and evaluating the isstring returns false

So if we format the escape result to string, it seems working fine.

.a {
  @svg: %('%s', escape('<svg></svg>'));
  --var: "data:image/svg+xml,@{svg}";
}

HelloWorld017 avatar Jul 05 '23 08:07 HelloWorld017

@HelloWorld017 : So if we format the escape result to string, it seems working fine.

.a {
  @svg: %('%s', escape('<svg></svg>'));
  --var: "data:image/svg+xml,@{svg}";
}

The escape function is documented as returning the string in literal form - i.e. without quotes. That is a hard guarantee of its output. Therefore, the following should be functionally equivalent to your example - just without all the additional run-around:

.a {
  @svg : escape("<svg></svg>");
  --var : "data:image/svg+xml,@{svg}";
}

rjgotten avatar Jul 05 '23 10:07 rjgotten

Therefore, the following should be functionally equivalent to your example - just without all the additional run-around:

But even though it is quoted using the "@{svg}" interpolation, it does not work with the error "SyntaxError: Invalid % without number", just like my first example.

HelloWorld017 avatar Jul 05 '23 10:07 HelloWorld017

Finally i use that trick : Escaping the color string directly give the syntax error, but assign the color string to a variable then escaping the variable works. This may help to understand why...

Not working escape > throw syntax error :

@icone-arrow-next : "data:image/svg+xml;utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 49 37'><path fill='@{color-icone}' d='M31.1 0l-1.5 1.5 15.5 16H0v2h45.1l-15.5 16 1.5 1.5L49 18.5z'/></svg>";

selector {
    @color-icone : escape( #ffffff ) ;
    background-image:url( @icone-arrow-next ) ;
}

Working :

@icone-arrow-next : "data:image/svg+xml;utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 49 37'><path fill='@{color-icone}' d='M31.1 0l-1.5 1.5 15.5 16H0v2h45.1l-15.5 16 1.5 1.5L49 18.5z'/></svg>";

selector {
    @c : #ffffff ;
    @color-icone : escape( @c ) ;
    background-image:url( @icone-arrow-next ) ;
}

Corcules avatar Jul 05 '23 10:07 Corcules