View All Articles or articles on Technology

ASP.NET 5 MVC Bundling & Minification with Gulp, npm and Bower

ASP.NET 5 is a redesign and re-implementation of the foundations of ASP.NET itself. It has been built from the ground up to be a more lightweight, modular and future-proof version of ASP.NET built for the cloud and, in my own opinion, in a way that will entice in more developers to the ASP.NET ecosystem. More on that later.

For those of you who are used to working with prior versions of ASP.NET MVC though, some of the changes may be a bit confusing. I'll cover more of the changes as I get used to working with them, but for now this article aims to give you code to help with getting back a version of Bundling that you probably miss.

The old days

In the old days, we would use ASP.NET's Bundling and Minification platform to get our scripts and style files out into the web in a compiled, minified and versioned manner.

Code like the following was common-place:

bundles.Add(new StyleBundle("~/Css").Include(
    "~/Content/bootstrap.css",
    "~/Content/Site.css"
    ));

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

Accompanied by front-end code such as this:

<head>
        @Styles.Render("~/Css")
..
        @Scripts.Render("~/Js")
</body>

The new way

The new way does not involve server side bundling like that, instead, we use Gulp and Visual Studio 2015's new Task Runner to pre-compile our files.

Now then, before reading the code below note that this is my own preference, and you can modify it to do whatever you want. The code below does the following things:

  • Binds the Gulp tasks to different actions within Visual Studio. Primarily so that whenever you change your scripts and styles, the compilation will automatically run to be reflected in your browser.
  • Compiles all of the third-party dependencies (both JS and CSS) into vendor.js and vendor.css for inclusion
  • Compiles all of your scripts and LESS files (you can change this to anything you want) into client.js and client.css
  • Puts a gulp.watch running against your Front-End folder in your project to auto-recompile

Below is the gulpfile.js for all of the above to happen. Note that, just like you would in the old ASP.NET Bundling way, you will still have to update your dependencies as you add them into the script below. It also relies on you having the folder structure underneath the code.

/// <binding BeforeBuild='copy' Clean='clean' ProjectOpened='watcher' />

var gulp = require("gulp"),
    rimraf = require("rimraf"),
    flatten = require("gulp-flatten"),
    uglify = require("gulp-uglify"),
    minifyCss = require("gulp-minify-css"),
    concat = require("gulp-concat"),
    watch = require("gulp-watch"),
    less = require("gulp-less"),
    sourcemaps = require("gulp-sourcemaps"),
    fs = require("fs");

eval("var project = " + fs.readFileSync("./project.json"));

var paths = {
    bower: "./bower_components/",
    js: "./" + project.webroot + "/js/",
    css: "./" + project.webroot + "/css/",
    frontend: "./FrontEnd/"
};

gulp.task("clean", function (cb) {
    rimraf(paths.css, function () { });
    rimraf(paths.js, cb);
});

gulp.task("copy", ["clean"], function () {
    var vendorJs = [
        paths.bower + "jquery/jquery.min.js",
        paths.bower + "bootstrap/dist/**/bootstrap.min.js",
        paths.bower + "jquery-validation/jquery.validate.js",
        paths.bower + "jquery-validation-unobtrusive/jquery.validate.unobtrusive.js",
        paths.bower + "Chart.js/Chart.min.js"
    ];
    var vendorCss = [
        paths.bower + "bootstrap/dist/**/bootstrap.min.css"
    ];
    gulp.src(vendorJs)
        .pipe(sourcemaps.init())
            .pipe(concat('vendor.js'))
        .pipe(sourcemaps.write('maps'))
        .pipe(gulp.dest(paths.js));
    gulp.src(vendorCss)
        .pipe(concat('vendor.css'))
        .pipe(gulp.dest(paths.css));

    clientCompilation();
});

function clientCompilation() {
    var clientLess = paths.frontend + "**/Site.less";
    var clientJs = paths.frontend + "**/*.js";

    gulp.src(clientLess)
        .pipe(concat('client.css'))
        .pipe(less())
        .pipe(sourcemaps.init())
            .pipe(minifyCss())
        .pipe(sourcemaps.write('maps'))
        .pipe(gulp.dest(paths.css));

    gulp.src(clientJs)
        .pipe(sourcemaps.init())
            .pipe(concat('client.js'))
            .pipe(uglify())
        .pipe(sourcemaps.write('maps'))
        .pipe(gulp.dest(paths.js));
}

gulp.task("client", clientCompilation);

gulp.task("watcher", function () {
    gulp.watch(paths.frontend + "**/*.{js,less}", ['client']);
});