Sunday, February 8, 2015

Gulp and bower in ASP.NET MVC

There are many tools, libraries and extensions for Visual Studio to do front end web development. There are NuGet packages that can help combine and minify JS and CSS (Microsoft Ajax Minifier), sass and less transformers (BundleTransformer.SassAndScss, BundleTransformer.Less), reactjs integration (React.Web.Mvc4), angular templates bundling (AngularTemplates.Compile). But there is another very efficient way to do front end development using nodejs, gulp and bower. In my experience it feels more natural than NuGet and MSBuild.

First of all we need to install nodejs: http://nodejs.org/download/. It will also install npm package manager. After that we need to install bower and gulp (don’t forget -g flag to install both packages globally):

npm install -g bower
npm install -g gulp
Now we’re ready to start using these tools with ASP.NET MVC.


Bower


Bower (http://bower.io) will help us manage client side libraries. It stores a list of all dependencies in bower.json file. You can create it manually or just run bower init in console and follow a few steps to generate new bower.json file. Let’s say we want to add angular to our project:

bower install angular

By default bower installs all components to bower_components directory. If you’d like to change that just create .bowerrc file and specify another directory:

{
    "directory": "my_new_directory"
}
For more configuration options see http://bower.io/docs/config/. If you already have bower.json file in your project and need to restore all components, just run bower install command. Here is a sample of bower.json file:

{
  "name": "my_project_name",
  "version": "0.0.1",
  "authors": [],
  "description": "my project description",
  "private": true,
  "ignore": [
    "**/.*",
    "node_modules",
    "bower_components",
    "test",
    "tests"
  ],
  "dependencies": {
    "angular": ">=1.3.0",
    "angular-route": ">=1.3.0"
  }
}

Gulp


Gulp (http://gulpjs.com) allows to easily build front end assets: convert sass, less to css, combine and minify css and js, lint them, run unit tests, optimize images and much more. There is a huge ecosystem around it and many libraries that can do pretty much anything with your assets. As an example let's combine and minify JS and CSS.

To do it we need to init our npm app and install several npm modules:
1. npm init
2. npm install gulp gulp-concat gulp-if gulp-minify-css gulp-rev gulp-rev-replace gulp-uglify gulp-useref --save-dev
In order to automatically determine JS and CSS bundles without even using ASP.NET MVC bundling mechanism we're going to use gulp-useref module. We need to wrap our JS and CSS tags into special html comments in our .cshtml files:

<!-- build:css /assets/site.css -->
<link href="/assets/components/foundation/css/normalize.css" rel="stylesheet"></link>
<link href="/assets/components/foundation/css/foundation.css" rel="stylesheet"></link>
<link href="/assets/components/metrics-graphics/dist/metricsgraphics.css" rel="stylesheet"></link>
<link href="/assets/styles/app.css" rel="stylesheet"></link>
<!-- endbuild -->

gulp-useref will cut css links between these html comments, combine css files into one file, minify it and replace with one single link tag that will point to /assets/site.css. The same rules apply to JS assets. Here is full gulp script listing:


var gulp = require('gulp');
var useref = require('gulp-useref');
var uglify = require('gulp-uglify');
var gulpif = require('gulp-if');
var minifyCss = require('gulp-minify-css');
var rev = require('gulp-rev');
var revReplace = require('gulp-rev-replace');

// Concat & Minify JS/CSS
gulp.task('build_assets', function () {
  var assets = useref.assets({
    searchPath: '.'
  });
  gulp.src('Views/**/*.cshtml', {
      base: './'
    })
    .pipe(assets)
    .pipe(gulpif('*.css', minifyCss()))
    .pipe(gulpif('*.js', uglify()))
    .pipe(rev())
    .pipe(assets.restore())
    .pipe(useref())
    .pipe(revReplace({
      replaceInExtensions: ['.cshtml']
    }))
    .pipe(gulp.dest('./obj/Release/Package/PackageTmp'));
});

gulp.task('default', ['build_assets']);

We definitely want to run this script from Visual Studio. We can add gulp script execution to Pre/Post build events. But when we let's say publish our solution to local folder we need to add a bit of xml to our .csproj file. Just add it before project closing tag:


<Target Name="RunGrunt" AfterTargets="CopyAllFilesToSingleFolderForPackage">
  <Exec Command="gulp" />
</Target>

You'll find combined/minified css and js files in published folder as well as modified .cshtml files. This technique allows to get rid of ASP.NET MVC bundles that are going to be deleted in the next version of MVC. See this github issue: https://github.com/aspnet/Mvc/issues/1577.


Please find working example of ASP.NET MVC project with configured gulp and bower support here: https://github.com/vadimi/gulp-aspnet-sample. Run npm install, bower install, open solution in Visual Studio and hit "Publish".

1 comment:

  1. Great article, but how will this work with MVC? The index.cshtml file gets modified with the new hashes in the file names, but when you publish using WebDeploy to a remote server, this doesn't work.

    ReplyDelete