CategoryOptimization

Running gulp tasks on a buildserver

With the newest ASP.NET release coming, Microsoft is removing their own optimization framework and pushes developers to use Gulp, NPM and Bower.
I do not want to manually minify and bundle my css and js files, so I want a Gulp task to do it.
My NPM file (package.json) looks like:

{
  "version": "1.0.0",
  "name": "ASP.NET",
  "private": true,
  "devDependencies": {
    "bower": "1.7.7",
    "gulp": "3.9.1",
    ....
  }
}

My bower file (bower.json) looks like

{
  "name": "ASP.NET",
  "private": true,
  "dependencies": {
    "jquery": "2.2.3",
    "jquery-validation-unobtrusive": "3.2.6",
    "bootstrap": "3.3.6",
    ....
  }
}

I also do not want my bundles to be source controlled.
It is a task of the buildserver to prepare my solution for release.
This means that the buildserver should be able to run the same Gulp tasks as we do in our development environment.

The following software should be installed on the buildserver to let the buildserver run Gulp tasks:

When installing Git, set install option to “Run GIT from the Windows Command Prompt”.
I’d like to have all my configuration source controlled, so I create a targets file which contains the targets for running Npm, Gulp and Bower and I import this file in my web project.

  <!-- Gulp -->
  <Target Name="RunGulpTask" AfterTargets="BowerInstall" Condition="'$(RunGulpTasks)' != ''">
    <Message Text="Running gulp task 'default'" Importance="high" />
    <Exec Command="node_modules\.bin\gulp" WorkingDirectory="$(ProjectDir)" />
  </Target>

  <!--Bower -->
  <Target Name="BowerInstall" AfterTargets="NpmInstall" Condition="'$(RunGulpTasks)' != ''">
    <Message Text="Running bower install" Importance="high"/>
    <Exec Command="node_modules\.bin\bower install" WorkingDirectory="$(ProjectDir)" />
  </Target>

  <!--Npm -->
  <Target Name="NpmInstall" BeforeTargets="BeforeBuild" Condition="'$(RunGulpTasks)' != ''">
    <Message Text="Running npm install" Importance="high"/>
    <Exec Command="npm install" WorkingDirectory="$(ProjectDir)" />
  </Target>

I do not want these tasks to run in my development environment (because the TaskRunner of Visual Studio takes care of it), so I added a RunGulpTask parameter. When this is provided (by adding /p:RunGulpTask=true to the msbuild command), the targets will be run before the solution is built.

My gulpfile.js looks like:

var gulp = require('gulp'),
.....
gulp.task('default', ['bundleCss', 'minCss', 'bundleJs', 'minJs'], function() {});

I did not provide a Gulp task to run, so Gulp will run the default task by convention. My default task has a dependency to all tasks I want to be run on the buildserver.
The buildserver now bundles and minifies my css and js files by using the same Gulp tasks.

Creating and maintaining minified files in Visual Studio 2012 (with Web essentials)

Web essentials brings a lot of cool features to Visual Studio. It is a creation of Mads Kristensen. Many of these features will be added to Visual Studio in future releases and Web Essentials is downloadable as nuget package.
One of the cool features of Web Essentials is minification and bundling.
When you right click on a js file, Web essential adds an option to the context menu called “Minify Javascript file(s)”.
This creates two files, one minified file (adds min.js to the original filename) and a map file (adds min.js.map to the original filename).
Map files can be used to make the minified file readable and debuggable. The creation of a map file can be enabled or disabled in Tools > Options.

But now the coolest feature, when you make changes to the original javascript file, the minified file gets updated automatically.

I use this features a lot for my javacript plugins (see http://www.vicreative.nl/Projects). I work in the original files, the minified files are updated automatically and I commit them all in Git.

When selecting more than one file, the option “Create Javascript bundle file” gets enabled. This creates the following files:

  • a bundle file, an xml file containing all filenames for this bundle.
  • a javascript file
  • a minified javascript file
  • a map file

The same features also work for CSS files, but no map is created because this is not necessary for css files.

Creating bundles can also be done automatically, read this post.

Update:
The bundling and minification has moved from Web essentials to a different NuGet package: Bundler & Minifier

Automatic Bundling and Minification in .Net

One of the cool new features of .Net 4.5 is the bundling and minification feature. This can save so many requests to your server and saves you many kbs without creating a debug hell. The amount of data is getting more and more important, because of the mobile devices. People pay lots of money on data bundles for their mobile device, we better not waste it. It also decreases loading time!! Reasons enough to start using bundling and minification!

First thing you need to do is download and install the Microsoft ASP.NET Web  Optimization Framework nuget package.

The bundles are registered at application startup. This event is handled in the Application_Start method in the Global.asax. I always try to keep my Global.asax as clean as possible and split all registrations to seperate files in the App_Start folder. So I created a static BundleConfig class in the App_Start folder. This class contains one method, Register with the current Bundles as argument.

BundleConfig.RegisterBundles(BundleTable.Bundles);

BundleConfig

The BundleConfig class registers all JavaScript and Stylesheet bundles, Both Bundles can be added to the same BundleCollection, but both bundles have their own classes. JavaScript bundles are created by using the ScriptBundle class and Stylesheet Bundles are created by using the StyleBundle class.

bundles.Add(new ScriptBundle("~/bundles/jquery").Include("~/Scripts/jquery-{version}.js"));

This creates a JavaScript bundle named “~/bundles/jquery”  and includes one file, “~/Scripts/jquery-{version}.js”.  {version} is automatically resolved to the version of the jquery file in the scripts folder.

bundles.Add(new StyleBundle("~/Content/css").Include("~/Content/site.css"));

This creates a Stylesheet bundle names “~/content/css”” and contains one file “~/Content/site.css”. Adding more files is easy, just add more files to the include and separate them by a comma.

Adding bundles to a view

The only thing left to do, is adding the bundles to a view.

MVC (Razor)

@Scripts.Render("~/bundles/jquery")

WebForms

<%: Scripts.Render("~/bundles/jquery") %>

The above code renders the JavaScript bundle.

MVC (Razor)

@Styles.Render("~/Content/css")

WebForms

<%: Styles.Render("~/Content/css") %>

The above code renders the Stylesheet bundle.

All the requests to the bundles have a querystring parameter (v=CkVTG71m7lHB5jSCpyOSxbeCVJLIPag7u7NL4ykFenk1). This is to make sure that no old versions of this file are cached and retrieved.

Config files

Bundles can also be configured in config files. This allows you to make changes to your bundle without having to rebuild the project. It also allows frontend developers to easily include, remove or change files in a bundle without having to use visual studio.

An example of a bundle.config file

<?xml version="1.0" encoding="utf-8" ?>
<bundles version="1.0">
  <styleBundle path="~/Content/css">
    <include path="~/Content/Site.css" />
  </styleBundle>
</bundles>

Debugging

One of the biggest problems of minified files is debugging. It’s almost impossible to debug those files.

That’s when the best feature of this minification framework comes in place. When compilation is set to debug (in web.config), the files will not be minified, which makes it a lot easier to debug.

<system.web>
    <compilation debug="true" />
    <!-- Lines removed for clarity. -->
</system.web>

Creating custom bundle transforms

Custom transforms can be created by implementing the IBundleTransform interface.,

using System.Web.Optimization;

public class MyByndleTransform : IBundleTransform
{
   public void Process(BundleContext context, BundleResponse response)
   {
       // Process bundle tranform here
   }
}

The custom transform can now be added to the transform of a bundle

var myBundle = new Bundle("~/My/Files/To/Include");
lessBundle.Transforms.Add(new MyBundleTransform());

This bundle can now be added to the bundles collection in the BundleConfig file.

Why creating custom bundle transforms? Just think of less or coffeescript, or whatever bundle you want.