Using ServiceStack bundler

Bundler by ServiceStack is a solution for bundling, minifying and compiling client script (JavaScript/CoffeScript(!)) and style sheets (CSS, LESS and SASS). There are a lot of this sort of solutions out there but I really like this one, hopefully you will too after reading this. At the projects github page you can find some really good documentation but I will write this post anyway and hopefully it would be even clearer how to use Bundler in your solution.

Bundler is implemented in JavaScript and runs through node.js. Bundler its lightweight, easy to configure/customize and simple to get into your own build process. Bundler will out of the box run in a ASP.NET MVC solution and with some tweaking it can run with web forms too. But that we don’t need to do now when we have MVC support within EPiServer 7 (preview) Smile

Install

Bundler is available as a Nuget package, you could probably install it manually by downloading the source from github but I will go with Nuget for now:

687474703a2f2f7777772e73657276696365737461636b2e6e65742f696d672f6e756765742d62756e646c65722e706e67

After the installation you will have some new folders and a new c# file (Mvc.Bundler.cs) in your solution.

1

The bundler-folder is where Bundler lives, here we have a full distribution of node.js (node.exe), the source for bundler (bundler.js), all the modules that bundler relies on and a Visual Studio 2010 extension(!).

The Visual Studio extension is quite useful for local development but I have experienced some problems with it, more on local development later. To install the extension just double click the BundlerRunOnSave.vsix and restart Visual Studio. After the install Bundler will run each time you save a related file.

Configure it!

My file structure for this example looks like this:

2

Bundler will by default look in the folders “~/Content” and “~/Scripts” for “bundle configurations”, this can be configured in “~/bundler/bundler.cmd”. You can see that I have defined two bundles “app.css.bundle” and “app.js.bundle” this is the configuration for which files that should belong to which bundle. The configuration looks like this:

app.css.bundle:

Normalize.css
Site.css
Startpage.less

app.js.bundle:

jquery-1.7.2.js
Bootstrapper.coffee
Startpage.js

The bundles can contain both JavaScript and CoffeeScript files or CSS, Less or Sass files. When Bundler runs it will compile and generate the bundles to something most browsers can understand, JavaScript and CSS!

The client code

For this post I have created some simple CSS/Less and JavaScript/CoffeeScript just to demonstrate how bundler works. The CSS will first normalize the default appearance to get almost the same default behavior in different browsers, set some background colors, add some colors and font sizes to the start page´s h2 and li-elements.

The JavaScript will run when the DOM is loaded and overwrite the color that has been set by the CSS for the start page´s h2 elements.

Normalize.css (view)

Site.css

body {
    background: #fff;
}
menu#TopMenu .selected {
    font-weight: bold;
}

Startpage.less

@listFontSize: 13px;
@listFontColor: #f00;

body#Startpage {
	ul {
		list-style-type:none; 
		li {
			font-size:@listFontSize;
			a {
				color:@listFontColor;
			}
		}
	}
}

jquery-1.7.2.js (view)

Bootstrapper.coffee

class Bootstrapper
	setup: ->
		bodyId = $("body").attr 'id'
		if bodyId is "Startpage"
			startPage = new Startpage
			startPage.load()
			return

($ document).ready ->
	strapper = new Bootstrapper
	strapper.setup()

Startpage.js

var Startpage = function () { };
Startpage.prototype.load = function () {
    $("h2").css("color", "green");
};

Adding references to bundles in “_Layout”-files

As I wrote earlier; a c#-file was added to the solution after the installation of Bundler, this file (Mvc.Bundler.cs) contains some html helpers for referencing bundles and some other sweet features. By using those extensions you will get support for:

  • Versioning of files for client caching
    • A version number is added as a querysting based on the files last write time so the client cache is dropped when new versions of your CSS/Javascript files are created.
  • Choose how to render your bundle. Will show how you can render all full files in debug mode but combined and minified in release mode. You can choose from:
    • Normal
    • Minified
    • Combined
    • MinifiedAndCombined
  • Renders the html for the script and css tags

The_Layout file:

@using ServiceStack.Mvc
@using EPiServer.Web.Mvc.Html
@using EPiBooks.Models

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>@ViewBag.Title</title>
    @if (HttpContext.Current.IsDebuggingEnabled) {
        @Html.RenderCssBundle("~/Content/app.css.bundle", BundleOptions.Normal)
        @Html.RenderJsBundle("~/Scripts/app.js.bundle", BundleOptions.Normal)
    } else {
        @Html.RenderCssBundle("~/Content/app.css.bundle", BundleOptions.MinifiedAndCombined)
        @Html.RenderJsBundle("~/Scripts/app.js.bundle", BundleOptions.MinifiedAndCombined)
    }
</head>
<body id="@ViewBag.BodyId">
    @{Html.RenderEPiServerQuickNavigator();}
    <section id="MainSection">  
        @Html.Partial("TopMenu", new NavigationContext())
        @RenderBody()
    </section>
</body>
</html>

The layout checks if the application is in debug mode (debug is set to true in web.config) and then renders all the css and js files without minifying them otherwise (debug is set to false in web.config) all js and css files and minified and combinde into single files.

A cool thing is that Bundler already has created the files needed as physical files on disk so no code is executed at runtime for rendering the js or css files. Files is just fetched as physical files from the servers disk.

Building for production and local development

Because the files are pre-generated and not generated at runtime we need to generate the CSS and JavaScript files each time we do a change when developing or releasing to test/production environments.

Local development

For local development Bundler ships with a Visual Studio extension that generates new files each time a js, css, less, sass, coffee or bundle file is edited. The VS-extension is pretty sweet but I have notice that Visual Studio will crash if you create a major syntax error in your JavaScript file and then hit ctrl + s, probably this will be fixed.

Because of this we could instead generate the files in compile-time, this requires that the project is rebuilt every time you make a change to a js, css, less, sass, coffee or bundle file. This is not that strange for us backend developers but I can only imagine what the UI developers will say about that Smile

I created a simple one-liner-PowerShell-script that execute Bundler through Node:

&"..\bundler\node.exe" "..\bundler\bundler.js" "..\Content" "..\Scripts"

Then added a post-build event:

3

Now every client resource is compiled, bundled and minified into JavaScript and CSS when the project is compiled.

Building for remote environments

When our build server is building to remote environments like test and production we also need to run Bundler for compiling and minifying. Fortunately it is as simple as adding that one-liner-PowerShell-script to your build process. Lets say you where building to your remote server with Psake then it would look something like this:

properties {
	$BuildConfiguration = 'Release'
	$CssFilesRoot = "Content"
	$WebScriptFilesRoot = "Scripts"
	$ApplicationSource = '..\MvcCiTest'
	$ApplicationSlnFile = '..\MvcCiTest.sln'
}

task Default -depends CopyFiles

task Staging -depends DeployWebToStagingFtp 

task DeployWebToStagingFtp -depends BackupWebAtStagingFtp {
	# deploy to staging
}

task BackupWebAtStagingFtp -depends MergeConfiguration {
	# backup staging before deploy
}

task MergeConfiguration -depends CopyFiles { 
	# merge configuration
}

task CopyFiles -depends Test {
	# copy needed files
}

task Test -depends Compile, Setup { 
	# run unit tests
}

task Compile -depends Setup { 
	Exec {
		msbuild $ApplicationSlnFile /t:Clean /t:Build /p:Configuration=$BuildConfiguration /v:q /nologo	
	}
	&"$ApplicationSource\bundler\node.exe" "$ApplicationSource\bundler\bundler.js" "$ApplicationSource\$CssFilesRoot" "$ApplicationSource\$WebScriptFilesRoot"
}

task Setup { 
	# do stuff needed for the release
}

The results

As I said; this is a really simple UI… so it looks like this Smile

4

But the cool thing is of course the bundling, compiling and minifying!

Debug mode

5

Production mode

6

Complete source

You can view the complete source at github.

Thanks for reading!

5 comments

  1. This is some really great stuff, Anton! Keep it up! :-)

  2. ubixar · · Reply

    Agreed, this is an excellent step-by-step guide!

    That I’ve even added to Bundler’s home page :)

    https://github.com/ServiceStack/Bundler/blob/master/README.md#community-resources

  3. Thanks guys, and thanks to the ServiceStack team for bringing us Bundler

  4. Thanks, great article. After build, sometime I can see original css fle has been changed. This make extra change history in version control system. How can I avoid it? Thanks!

    1. Hi, glad you liked the article. When you are experiencing the change of the original file what kind of diff does the version control system report? I have not experienced that issue myself.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: