Bundling and Minifying in Optimizely and ASP.NET Core

WebCompiler used to be a very useful extension to Visual Studio but is no longer supported. A great replacement is WebOptimizer, an open source project recommended by Microsoft. WebOptimizer is used as a middleware which means it operates at runtime, so no hassle with build configuration. The first request will trigger WebOtimizer to bundle and minify files which then will be cached for further requests. In this post I will describe how to set up WebOptimizer to bundle and minify javascript files as well as bundle, minify and compile scss files. I will also demonstrate code that creates markup with links to assets without bundling and minification, for the development environment

Optimizely

WebOptimizer is in this post used with Visual Studio 2022 and ASP.NET Core 5.0. The project in which I used WebOptimizer is built upon a CMS called Optimizely, formerly known as Episerver. This content of this post is however applicable for other project types as well.

Installation

WebOptimizer is installed as a nuget package, LigerShark.WebOptimizer.Core (3.0.357 latest and used in my project). In order to compile sass, a separate nuget has to be installed, LigerShark.WebOptimizer.Sass (3.0.84).

Filestructure

In this example the filestructure of .js and .scss files are as follows (variables.scss is assumed to be included in the other two .scss files):

  • wwwroot
    • sass
      • variables.scss
      • base.scss
      • colors.scss
    • js
      • script1.js
      • script2.js

Startup.cs

Note that this Startup is not complete, It containes code to show how I use WebOptimizer but not much more. In order to keep Startup clear, I've introduced a separate extension method, AddBundleAndMinify().


using MyMiddleware;

public class Startup
{
  public IConfiguration Configuration { get; }
  public IWebHostEnvironment Env { get; }

  public Startup(IConfiguration configuration, IWebHostEnvironment env)
  {
    Configuration = configuration;
    Env = env;
  }

  public void ConfigureServices(IServiceCollection services)
  {
    services.AddControllersWithViews();
    services.AddBundleAndMinify(Env.IsDevelopment());
  }
  public void Configure(IApplicationBuilder app)
  {
    //insert right before UseStaticFiles();
    app.UseWebOptimizer();
    app.UseStaticFiles();
  }
}

AddBundleAndMinify()


namespace MyMiddleware
{
  public static class ServiceCollectionExtenrions
  {
    public static IServiceCollection AddBundleAndMinify(this IServiceCollection services, bool development)
    {
      services.AddWebOptimizer(pipeline =>
      {
        pipeline.AddJavaScriptBundle("/site.js",
          "/js/script1.js",
          "/js/script2.js",
          "/js/script3.js");

        //omit the .scss extension. The order of the files will be preserved.
        string[] files = {
          "/sass/base",
          "/sass/colors"
        };

        pipeline.AddScssBundle("/site.css", files.Select(file => file + ".scss").ToArray());
                
        //In development, create a css-bundle for each scss file. 
        if (development)
        {
          var option = new Sass.WebOptimazerScssOptions() { MinifyCss = false };
          files.ToList().ForEach(file => {
            pipeline.AddScssBundle(option, file + ".css", file + ".scss");
          });
        }

      });

      return services;
    }
  }
}

_ViewImports.cshtml

Include these lines to get access to tag helpers that comes with WebOptimizer. Tag helpers in this project are used for:

  • Add version number at the end of bundles. The number will change with every build and will force the browser to request the bundle again and not use the browser cache.
  • Create unbundled links to assets while in development mode.


@using WebOptimizer
@addTagHelper *, WebOptimizer.Core

_Layout.cshtml


<!-- in head element -->
<link href="/site.css" rel="stylesheet" type="text/css" />

<!-- just before end of body -->
<script src="/site.js"></script>

appsettings.Development.json

  • enableCaching: false will avoid setting Response-Header Cache-Control and the browser will not cache. It's needed when enableTagHelperBundling is false and the assets type is CSS where a version number is not added at the end of each .css file.
  • enableTagHelperBundling: false will not bundle files referenced within script or link elements. Each file in the bundle will be referenced separately.
Default values for both of these attributes are true.

{
  "webOptimizer": {
    "enableCaching": false,
    "enableTagHelperBundling": false
  }
}

Compression

If compression is used (services.AddResponseCompression) special care has to be taken in order to compress bundled or minified files, see documentation of WebOptimizer .

Links