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
Best Way to Use Media Queries for Mobile Designs
Bind Angularjs Variables into CSS Syntax
How to Change Svg Fill Color When Used as Base-64 Background Image Data
How to Set CSS Width Equal to Length of Longest Text
What Is the Different Between Clearfix Hack and Overflow:Hidden VS Overflow:Auto
Rtl Is on Web Page Reverses Numbers with a Dash
Float: Right in IE7 Dropping to a New Line
How to Remove the Blue Halo Glow from Jquery Mobile Input Elements That Receive Focus
Css3 Animation Is Not Working in Ie11 But Works in Other Browsers
Can You CSS Blur Based on a Gradient Mask
Background Images Path in SASS and Compass
CSS Sticky Header/Footer and Fully Stretched Middle Area
How to Remove Clear Button ( 'X' Button ) from Ie10 Textboxes in Compatibility Mode