jvance.com

.net, c#, asp.net, linq, htpc, woodworking

Jarrett's Tech Blog - For August 2012

  1. ASP.NET MVC Bundling of Bootstrap Less Source

    Our team is utilizing bootstrap for our MVC application. We would like to be able to update the variables.less file and have the entire "theme" of the website updated.

    The typical methods are:

    • Utilize a tool (Chirpy in Visual Studio) to automatically output css anytime the less file is updated
    • Transform the less on the server side

    I prefer the second approach since:

    • It doesn't depend on everyone having the same tool setup
    • You don't have worry about versioning of the code generated files

    The most popular library dotless utilizes HttpHandlers to translate the less to css on the server side. This requires extra configuration in the web.config.

    However, the latest version of ASP.NET MVC, version 4, has support for bundling and minification. It also supports transforms.

    The asp.net article shows a few simple lines of code to add a transform for less files.

    using System.Web.Optimization;
    
    public class LessTransform : IBundleTransform
    {
        public void Process(BundleContext context, BundleResponse response)
        {
            response.Content = dotless.Core.Less.Parse(response.Content);
            response.ContentType = "text/css";
        }
    }
    

    Unfortunately, this method only supports simple less files. If there are any imports, like in bootstrap, the application will throw a FileNotFoundException.

    {"You are importing a file ending in .less that cannot be found.":"reset.less"}

    We need to be able to tell the parse method where to find the imported files. You can do that by providing a VirtualFileReader that looks in your less folder.

    internal sealed class VirtualFileReader : IFileReader
    {
        public byte[] GetBinaryFileContents(string fileName)
        {
            fileName = GetFullPath(fileName);
            return File.ReadAllBytes(fileName);
        }
    
        public string GetFileContents(string fileName)
        {
            fileName = GetFullPath(fileName);
            return File.ReadAllText(fileName);
        }
    
        public bool DoesFileExist(string fileName)
        {
            fileName = GetFullPath(fileName);
            return File.Exists(fileName);
        }
    
        private static string GetFullPath(string path)
        {
            return HostingEnvironment.MapPath("~/less/" + path);
        }
    }
    

    Now all you need to do is provide the config with the VirtualFileReader to the parse method like so:

    public class LessTransform : IBundleTransform
    {
        public void Process(BundleContext context, BundleResponse response)
        {
            DotlessConfiguration config = new DotlessConfiguration();
            config.MinifyOutput = false;
            config.ImportAllFilesAsLess = true;
            config.CacheEnabled = false;
            config.LessSource = typeof(VirtualFileReader);
    #if DEBUG
            config.Logger = typeof(DiagnosticsLogger);
    #endif
            response.Content = Less.Parse(response.Content, config);         
            response.ContentType = "text/css";
        }
    }
    

    Perfect! With just a single reference to bootstrap.less you can have your less files utilize the existing bundling and minification strategy.

    public class BundleConfig
    {
        public static void RegisterBundles(BundleCollection bundles)
        {
            var less = new Bundle("~/bundles/less")
                .Include("~/less/bootstrap.less")
                .Include("~/less/datepicker.less")
            less.Transforms.Add(new LessTransform());
            less.Transforms.Add(new CssMinify());
            bundles.Add(less);
    ...
    
    Posted by JarrettV on August 31 at 8:09 PM

© Copyright 2024