How to Output Errors When Using .Less Programmatically

How can I output errors when using .less programmatically?

The dotLess parser traps Exceptions and outputs them to a Logger. The snippet from dotLess's source that performs this is LessEngine.TransformToCss:

public string TransformToCss(string source, string fileName)
{
try
{
Ruleset ruleset = this.Parser.Parse(source, fileName);
Env env = new Env();
env.Compress = this.Compress;
Env env2 = env;
return ruleset.ToCSS(env2);
}
catch (ParserException exception)
{
this.Logger.Error(exception.Message);
}
return "";
}

Less.Parse has an overload that takes a DotlessConfiguration object, which provides several properties that you can use:

public class DotlessConfiguration
{
// Properties
public bool CacheEnabled { get; set; }
public Type LessSource { get; set; }
public Type Logger { get; set; }
public LogLevel LogLevel { get; set; }
public bool MinifyOutput { get; set; }
public int Optimization { get; set; }
public bool Web { get; set; }
}

You will notice that the Logger property is of type Type. Whatever type you supply must implement dotless.Core.Loggers.ILogger:

public interface ILogger
{
// Methods
void Debug(string message);
void Error(string message);
void Info(string message);
void Log(LogLevel level, string message);
void Warn(string message);
}

As we saw in the first snippet, the Error method on the logger will get called when an error is encountered during parsing.

Now, the one sticky point of all this is how exactly an instance of the type that implements ILogger gets instantiated. Internally, dotLess uses an IoC container that is baked into the DLL. Following the method calls, it appears that it will eventually call Activator.CreateInstance to instantiate your ILogger.

I hope this is at least somewhat helpful.

dotlesscss does not show errors

You can do this very easily with web.config. In your dotless configuration section, add the following: logger="dotless.Core.Loggers.AspResponseLogger". This will make dotless output the errors instead of blank css.

I've included the following as an example. ("..." represents existing stuff in your web.config). In my example below cache is set to false. This is useful for debugging purposes. It should probably be set to true under normal circumstances.

<configuration>    
<configSections>
...
<section name="dotless" type="dotless.Core.configuration.DotlessConfigurationSectionHandler,dotless.Core" />
</configSections>

<dotless minifyCss="false" cache="false"
logger="dotless.Core.Loggers.AspResponseLogger" />
...
</configuration>

Log .less errors to console

If you're using Visual Studio, you can try using Web Essentials extension, which is free on the gallery. This add-in compiles .LESS on save, and gives you the errors in the output console. You can also see the compiled .css side-by-side as as you edit the .LESS.

Import LESS from server

I found the easiest solution was to implement IFileReader.

The implementation below makes a HTTP request to any LESS path prefixed with "~/assets", otherwise we use the default FileReader.

Note that this is prototype code:

public class HttpFileReader : IFileReader
{
private readonly FileReader inner;

public HttpFileReader(FileReader inner)
{
this.inner = inner;
}

public bool DoesFileExist(string fileName)
{
if (!fileName.StartsWith("~/assets"))
return inner.DoesFileExist(fileName);

using (var client = new CustomWebClient())
{
client.HeadOnly = true;
try
{
client.DownloadString(ConvertToAbsoluteUrl(fileName));
return true;
}
catch
{
return false;
}
}
}

public byte[] GetBinaryFileContents(string fileName)
{
throw new NotImplementedException();
}

public string GetFileContents(string fileName)
{
if (!fileName.StartsWith("~/assets"))
return inner.GetFileContents(fileName);

using (var client = new CustomWebClient())
{
try
{
var content = client.DownloadString(ConvertToAbsoluteUrl(fileName));
return content;
}
catch
{
return null;
}
}
}

private static string ConvertToAbsoluteUrl(string virtualPath)
{
return new Uri(HttpContext.Current.Request.Url,
VirtualPathUtility.ToAbsolute(virtualPath)).AbsoluteUri;
}

private class CustomWebClient : WebClient
{
public bool HeadOnly { get; set; }
protected override WebRequest GetWebRequest(Uri address)
{
var request = base.GetWebRequest(address);
if (HeadOnly && request.Method == "GET")
request.Method = "HEAD";

return request;
}
}
}

To register the reader, execute the following when your application starts:

var configuration = new WebConfigConfigurationLoader().GetConfiguration();
configuration.LessSource = typeof(HttpFileReader);

How to detect and print changing variables LESS

update
When you allow the compiled styles are directly applied on your page, you can simply call `modifyVars as follows:

<!DOCTYPE html>
<html>
<head>
<title>Less Example</title>
<script>
less = {
env: "development"
}
</script>
<link rel="stylesheet/less" type="text/css" href="t.less">
<script src="//cdnjs.cloudflare.com/ajax/libs/less.js/2.5.0/less.min.js"></script>
<script>

function writeCSS(){
less.modifyVars({
'colore-body': document.getElementById('choose-color').value
});
}

</script>
</head>
<body>
<input type="text" id="choose-color">
<button onclick="writeCSS()">aggiorna</button>
<div id="lesscode"></div>
</body>
</html>

Demo: http://plnkr.co/14MIt4gGCrMyXjgwCsoc

end update

Based on How to show the compiled css from a .less file in the browser?, How to update variables in .less file dynamically using AngularJS and Less: Passing option when using programmatically (via API) (you should also read: http://lesscss.org/usage/#programmatic-usage) you should be able to use the code like that shown below:

<!DOCTYPE html>
<html>
<head>
<title>Less Example</title>
<script>
less = {
env: "development"
}
</script>
<script src="//cdnjs.cloudflare.com/ajax/libs/less.js/2.5.0/less.min.js"></script>
<script>

function writeCSS(){
var lessCode = '';
var xmlhttp = new XMLHttpRequest();

xmlhttp.onreadystatechange = function(){
if(xmlhttp.status == 200 && xmlhttp.readyState == 4){
var options = {}
options['modifyVars'] = {'colore-body' : document.getElementById('choose-color').value}
lessCode = xmlhttp.responseText;
less.render(lessCode, options, function (error, output) {
if(!error) {
document.getElementById('lesscode').innerHTML = output.css;
}
else document.getElementById('lesscode').innerHTML = '<span style="color:red">' + error + '</span>';
});

}
};
xmlhttp.open("GET","styles.less",true);
xmlhttp.send();
}
</script>
</head>
<body>
<input type="text" id="choose-color">
<button onclick="writeCSS()">aggiorna</button>
<div id="lesscode"></div>
</body>
</html>

demo: http://plnkr.co/YbdtOwOeQPC1k9Vq4ZBv

And finally based on Force Cache-Control: no-cache in Chrome via XMLHttpRequest on F5 reload you can prevent caching of your source file with the following code:

xmlhttp.open("GET","t.less?_=" + new Date().getTime(),true);  

In the above the env: "development" setting prevents your source files from caching. To clear the cache otherwise, you should call less.refresh(true) before your less.render call.

i have another little problem, if in my less file there is a reference
to another less file like this(@import "another.less") script doesn't
work.

Make sure that another.less in the above is in the same folder as your styles.less file. Notice that import (when using less in browser) are read with a XMLHttpRequest too. So your imported files should be readable by browser and their paths should be relative to the styles.less file. Also see http://lesscss.org/usage/#using-less-in-the-browser-relativeurls

LESS Compiler: Unexpected token u

Go to %USERPROFILE%\AppData\Local\Microsoft\VisualStudio\12.0\Extensions which is the folder where per-user Visual Studio extensions reside. WebEssentials will be located in a subfolder with a randomly generated name.

From inside the WebEssentials folder, open up the file Resources\nodejs\tools\server\services\srv-less.js and go to line 65, which reads:

map = JSON.parse(output.map);

The problem is source map output may be the undefined value. JSON.parse can only parse strings, so it casts that to the string value "undefined" before parsing, but JSON does not recognize that as valid token. (It only understands the null value, not the undefined value.)

So... change line 65 to read:

map = JSON.parse(output.map || "null");

And voilà; LESS compilation on files with empty output works again.

Source:
https://github.com/madskristensen/WebEssentials2013/issues/1696



Related Topics



Leave a reply



Submit