— Tech+Life+Music

Setting up dotless with automatic LESS CSS compilation on build

If you want to use the LESS dynamic CSS language in your .NET web applications, using the dotless (formally .less) library is the popular way to go, so that your .less files are compiled server-side.

While dotless provides a no-brainer way to serve compiled .less files through its custom HttpHandler, this still forces the app to compile the .less files on every request (barring caching, etc). An arguably better way would be to pre-compile our .less files beforehand, so that we can work with LESS in development, while mindlessly serving minified CSS files in production.

This post will show how we can integrate the dotless package into your .NET web application, do all that crap I just described above, and making sure we do them in an automatic manner. A true set-and-forget system.

Major props go to my colleague @camemay, whose work I’ve built upon.

Let’s get started!

Installing the dotless package

For this post, I’ll be using an ASP.NET MVC 3 web application as a scapegoat. Having a ready MVC 3 project in hand (a new one off Visual Studio would be fine), installing dotless is a breeze over NuGet.

install-package dotless

This does a few things off the bat, but aside from the usual auto-adding of installed assemblies, the changes of more concern are those that silently happened in your web.config:

  • The custom LessCssHttpHandler was registered in your httpHandlers declaration to handle *.less files.
  • The same handler was also registered against the system.webServer declaration.
  • A custom dotless configuration was defined, and a preliminary dotless config section was appended at the end of the file.

Let’s start by modifying the path="*.less" declarations (in two places) to path=".less.css". This allows us to open LESS files in Visual Studio automatically as CSS files, and therefore with CSS Intellisense. Take note that this modifies the handling declarations such that .less files won’t be handled by the dotless handler (they won’t be compiled!) — you have to write your LESS code in .less.css files.

If you’re particularly paranoid about it, you may opt to change verb="GET" to verb="GET,HEAD" as well.

<system.web>
    <!-- ... -->
    <httpHandlers>
        <add path="*.less.css" verb="GET" type="dotless.Core.LessCssHttpHandler, dotless.Core" />
    </httpHandlers>
</system.web>
<system.webServer>
    <!-- ... -->
    <handlers>
        <add name="dotless" path="*.less.css" verb="GET" type="dotless.Core.LessCssHttpHandler,dotless.Core" resourceType="File" preCondition="" />
    </handlers>
</system.webServer>

Setting up the automated LESS compilation script

Now that we’ve basically decided that LESS CSS code will be in *.less.css files, we can target that for automatic compilation.

The way we’re going to approach this is by writing a custom MSBuild script that executes the dotless compiler against *.less.css files, converting the LESS syntax to minified legal CSS, and renaming the files to *.min.css. These will be our files for production.

For most of this process, I’ve been referencing Ted Nyberg’s own post on the topic, with a few changes here and there.

Include the dotless compiler in your project

First thing we have to do is to include the dotless.compiler.exe in the project so we can refer to it later in the build script. If you followed this guide and installed dotless via NuGet, then this file will be in $(ProjectDir)/packages/dotless.x.x.x/tool. Just add this executable into your project. For purposes of this guide, I’ll be mirroring Ted’s folder naming convention as well, so I’ll be adding this into an [MSBuild] folder.

Write the build script to compile LESS

Next we’ll start writing our build script. I added an XML file into my [MSBuild] folder, and named it dotless.CompileLessToCss.targets. You can name it whatever you want, but by nature of what we’re going to be working with, a good practice is to name it as a *.targets file.

Let’s start with Ted’s script, and let’s work from there:

<?xml version="1.0" encoding="utf-8" ?>
<Project xmlns="http://schemas.microsoft.com/developer/MsBuild/2003">
  <Target Name="CompileDotlessCss">
    <ItemGroup>
      <Binaries Include="*.dll;*.exe"/>
    </ItemGroup>
 
    <!-- Compile dotLess CSS into minified full CSS -->
    <Exec Command="$(ProjectDir)..\[MSBuild]\dotless.Compiler.exe $(ProjectDir)..\css\main.css $(ProjectDir)..\css\main.min.css -m"/>
 
  </Target>
</Project>

This script will compile only one file in our project: main.css into main.min.css. Let’s modify this so that it’ll convert all *.less.css in our $(ProjectDir)..\css\ folder into their *.min.css counterparts.

We’ll have to make the build script identify all those *.less.css files. We can set up an ItemGroup declaration to do just that. Put this right before the <Target> declaration:

<ItemGroup>
    <LessFiles Include="css\*.less.css" />
</ItemGroup>

That aside, we can modify the Exec command to this:

<Exec Command="[MSBuild]\dotless.compiler.exe -m %(LessFiles.FullPath)" />

This will take care of running the dotless compiler against all our *.less.css files. Only thing left to do is to specify what filename to use for the compiled + minified CSS code (and not rely on the compiler’s defaults).

Unfortunately, there’s no easy way to call a string.replace on LessFiles.FullPath. I’m personally using the method described in this StackOverflow answer, but there are others suggested on the same page. Use the method most comfortable for you. This modifies my command to this:

<Exec Command="[MSBuild]\dotless.compiler.exe -m %(LessFiles.FullPath) $([System.String]::Copy('%(LessFiles.FullPath)').Replace('.less.','.min.'))" />

Lastly, add the AfterTargets="AfterBuild" property to the Target tag. This will tell MSBuild to run this script after the AfterBuild “event” in the project build process.

Our final build script will be as follows:

<?xml version="1.0" encoding="utf-8" ?>
<Project xmlns="http://schemas.microsoft.com/developer/MsBuild/2003">
    <!--    
    This MSBuild Script will compile all [*.less.css] files in the /CSS folder to their [*.min.css] counterparts.
    -->
    <ItemGroup>
        <LessFiles Include="css\*.less.css" />
    </ItemGroup>
    <Target Name="CompileDotlessCss" AfterTargets="AfterBuild">
        <ItemGroup>
            <Binaries Include="*.dll;*.exe"/>
        </ItemGroup>                
 
        <!-- Compile dotLess CSS into minified full CSS -->
        <Exec Command="[MSBuild]\dotless.compiler.exe -m %(LessFiles.FullPath) $([System.String]::Copy('%(LessFiles.FullPath)').Replace('.less.','.min.'))" />
 
    </Target>
</Project>

Invoking our script when the project builds

The last thing we have to do is to make sure our script gets run when the project actually builds. I’m not at all adept with MSBuild (crash courses! ugh!) so you might prefer doing this another way, but this is the method I found closest to heart. Feel free to adjust.

The .csproj file that defines your web application also has MSBuild instructions on how to build it on compile. We’ll just manually plug in directions to include our script.

To be able to edit the .csproj, we’ll first need to unload the project. Right-click on the project in your Solution Explorer, and choose to Unload Project.

With your project unloaded, right-click on the project again, and choose to edit it. You’ll be presented with delicious XML.

Somewhere in all that XML, just plug in an Import command:

<Import Project="[MSBuild]\dotless.CompileLessToCss.targets" />

I’m particularly paranoid, so I put this somewhere towards the end of the file.

And that’s it!

If we did everything correctly, our project should automatically compile our *.less.css into *.min.css whenever we build the project.

Try it out! Add a style.less.css into your project, and slap this inside:

@foo : #beefff;
 
body {
    background : @foo;
    div#main {
        padding : 2px;
    }
}

Compile your project, then navigate to style.less.css in your browser, and you should get the following:

body {
  background: #beefff;
}
body div#main {
  padding: 2px;
}

Navigating to style.min.css should net you:

body{background:#beefff}body div#main{padding:2px}

Use the *.less.css files for development and debugging, and the *.min.css files for production.

6 comments thrown in. Share your two cents.
  1. Dotless | Glamorosang Baboy says: June 7, 20137:08 pm

    [...] For more info, visit Less for .Net website or github page. Also, take the time to read this informative blog of Carding, a former colleague and an awesome mentor,  on how to Set up dotless with automatic LESS CSS compilation on build. [...]

  2. Michal Cwienczek says: August 4, 201312:19 am

    Nice tutorial,

    However the command didin’t work for me – the command parameters – paths didn’t have quotes around and thus resulting in Visual Studio saying Error Code -1. For anyone having the same problem just use MSBuild quote mark ( " ) around the paths like this:

  3. Wael Emara says: August 6, 20133:07 pm

    this worked just fine with me
    but i do have an issue when i do this the css generated files is not included in the project and then when i publish the web project they are not copied across

  4. Dot Net Training In Mumbai says: February 13, 20146:26 pm

    I feel dotless isn’t dependably upgarded with the most recent Less version.

  5. Richard Neil Ilagan says: February 13, 20146:44 pm

    @Dot Net Training ~ pretty much, yeah, I feel the same way. I’ve actually shifted to using Node to precompile my LESS stuff (or SASS) by leveraging Grunt. It works with any project type as well, not just C# or Node.

    I should update this post and reference that.

  6. Ben Sewards says: August 22, 20143:51 am

    Just a heads up: if you try to build with this exec command and do not have a .less.css file to execute, the path returned will be empty for source and will throw a path error.

    Solution: Either just add a .less.css file to begin with or adjust the exec command source path to fallback to a default value if empty is found.

Submit comment