Using Bower with Visual Studio 2013

Update 1 (Feb 23, 2015): Improved code in gulpfile.js

You may have heard that with ASP.NET 5 and Visual Studio 2015, Microsoft will be using Bower as a client-side package manager. That means that packages like jQuery, Bootstrap and Angular will no longer be referenced using NuGet. This might scare you initially, but I very strongly believe that this is a good thing.

Why is this a good thing?

NuGet is a great package manager for .NET libraries and the Microsoft eco-system. The web however, is much larger than Microsoft. The web development world has largely settled on Bower as the defacto package manager for client side libraries. We can’t expect a developer who creates a fancy new JavaScript library to publish their library as a NuGet package. That developer might have nothing to do with the Microsoft ecosystem and expecting them to learn it so we can use their package is just not reasonable. This brings us to the current state of client side packages on Nuget. Many packages are not available on Nuget. Equally as frustrating, some packages are available but are horribly out of date. This seems to be getting worse lately.

So…Given the fact that Microsoft is moving to Bower in the next version of ASP.NET, I have started migrating all my existing ASP.NET projects to use Bower for client side packages.

Here is a step-by-step guide to using Bower with Visual Studio 2013 using the MVC5 File->New Project template as an example.

Install Node and Bower

If you don’t already have it installed, download and install node.js.

Once node is installed, we need to install bower using the node package manager (npm). From the command line, run

npm install bower -g

Install Git

Yup..you’re going to need git. Bower uses git to download packages from github. Specifically, you will need to install msysgit and select the “Run Git from the Windows Command Prompt” option.

msysgit

Source: https://github.com/bower/bower

Initializing Bower for our project

First up, we will need to create a new bower.json file for our project. This is the file that will contain a list of all the packages your application depends on. The easiest way to do this is to run the bower init command in the your project folder:

The default settings are fine for our purposes. The only setting I changed was marking the project as private to avoid accidentally publishing my project to the public bower registry.

Replacing Nuget Packages with Bower Packages

A new MVC 5 project reference jQuery, jQuery Validation, Bootstrap and Modernizr. Let’s start by adding jQuery using Bower. Bower packages are installed from the command line using the bower install.

bower install jquery --save

By default, bower components are installed in the bower_components folder. I don’t include this folder in Visual Studio and I don’t check it in to source control. The contents are easily restored by calling the bower install command with no other options.

Next, we will install the remaining packages. We also need to install RespondJS. This is a responsive design polyfill that is included in the Nuget bootstrap package but is not included in the bower package.

bower install jquery-validation --save
bower install jquery-validation-unobtrusive --save
bower install modernizr --save
bower install bootstrap --save
bower install respond-minmax --save

Now, we have all our required client side components installed in the bower_components folder. The bower.json file describes all the client side packages that our project depends on.

{
"name": "BowerInVS2013",
"version": "0.0.0",
"license": "MIT",
"private": true,
"dependencies": {
"jquery": "~2.1.3",
"modernizr": "~2.8.3",
"jquery-validation": "~1.13.1",
"bootstrap": "~3.3.1",
"respond-minmax": "~1.4.2",
"jquery-validation-unobtrusive": "~3.2.2"
}
}

Getting files from bower_components

Some people will simply include the bower_components folder into the web application and reference the JavaScript / CSS files directly. A typical bower_components folder contains much more than we actually need. I prefer to copy only the files we need to a known location and reference them from there. This is a task that can be easily accomplished using a client side build system like Grunt or Gulp.

I prefer using Gulp these days so that’s what we will use here. To setup our project for Gulp, follow the instructions on my earlier blog post.

We will need the following node modules for this gulp file.

npm install gulp --save-dev
npm install gulp-concat --save-dev
npm install gulp-uglify --save-dev
npm install del --save-dev 
npm install gulp-bower --save-dev 
npm install gulp-minify-css --save-dev

Here is the gulpfile.js that I created to roughly mimic the bundles that are created by the MVC new project template. The comments in the file explain what each gulp task is doing.

/// 
// include plug-ins
var gulp = require('gulp');
var concat = require('gulp-concat');
var uglify = require('gulp-uglify');
var del = require('del');
var minifyCSS = require('gulp-minify-css');
var bower = require('gulp-bower');
var sourcemaps = require('gulp-sourcemaps');

var config = {
//JavaScript files that will be combined into a jquery bundle
jquerysrc: [
'bower_components/jquery/dist/jquery.min.js',
'bower_components/jquery-validation/dist/jquery.validate.min.js',
'bower_components/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js'
],
jquerybundle: 'Scripts/jquery-bundle.min.js',

//JavaScript files that will be combined into a Bootstrap bundle
bootstrapsrc: [
'bower_components/bootstrap/dist/js/bootstrap.min.js',
'bower_components/respond-minmax/dest/respond.min.js'
],
bootstrapbundle: 'Scripts/bootstrap-bundle.min.js',

//Modernizr
modernizrsrc: ['bower_components/modernizr/modernizr.js'],
modernizrbundle: 'Scripts/modernizer.min.js',

//Bootstrap CSS and Fonts
bootstrapcss: 'bower_components/bootstrap/dist/css/bootstrap.css',
boostrapfonts: 'bower_components/bootstrap/dist/fonts/*.*',

appcss: 'Content/Site.css',
fontsout: 'Content/dist/fonts',
cssout: 'Content/dist/css'

}

// Synchronously delete the output script file(s)
gulp.task('clean-vendor-scripts', function () {
return del([config.jquerybundle,
config.bootstrapbundle,
config.modernizrbundle]);
});

//Create a jquery bundled file
gulp.task('jquery-bundle', ['clean-vendor-scripts', 'bower-restore'], function () {
return gulp.src(config.jquerysrc)
.pipe(concat('jquery-bundle.min.js'))
.pipe(gulp.dest('Scripts'));
});

//Create a bootstrap bundled file
gulp.task('bootstrap-bundle', ['clean-vendor-scripts', 'bower-restore'], function () {
return gulp.src(config.bootstrapsrc)
.pipe(sourcemaps.init())
.pipe(concat('bootstrap-bundle.min.js'))
.pipe(sourcemaps.write('maps'))
.pipe(gulp.dest('Scripts'));
});

//Create a modernizr bundled file
gulp.task('modernizer', ['clean-vendor-scripts', 'bower-restore'], function () {
return gulp.src(config.modernizrsrc)
.pipe(sourcemaps.init())
.pipe(uglify())
.pipe(concat('modernizer-min.js'))
.pipe(sourcemaps.write('maps'))
.pipe(gulp.dest('Scripts'));
});

// Combine and the vendor files from bower into bundles (output to the Scripts folder)
gulp.task('vendor-scripts', ['jquery-bundle', 'bootstrap-bundle', 'modernizer'], function () {

});

// Synchronously delete the output style files (css / fonts)
gulp.task('clean-styles', function () {
return del([config.fontsout,
config.cssout]);
});

gulp.task('css', ['clean-styles', 'bower-restore'], function () {
return gulp.src([config.bootstrapcss, config.appcss])
.pipe(concat('app.css'))
.pipe(gulp.dest(config.cssout))
.pipe(minifyCSS())
.pipe(concat('app.min.css'))
.pipe(gulp.dest(config.cssout));
});

gulp.task('fonts', ['clean-styles', 'bower-restore'], function () {
return
gulp.src(config.boostrapfonts)
.pipe(gulp.dest(config.fontsout));
});

// Combine and minify css files and output fonts
gulp.task('styles', ['css', 'fonts'], function () {

});

//Restore all bower packages
gulp.task('bower-restore', function() {
return bower();
});

//Set a default tasks
gulp.task('default', ['vendor-scripts', 'styles'], function () {

});

To run the gulp tasks, simply run gulp from the command line. This should output a number of min.js files to the Scripts folder and some css and font files to the Contents/dist folder. In Visual Studio, include these new files into the project.

Removing NuGet Packages

Now that we have all the script and css bundles we need, we can uninstall the original NuGet packages.

Uninstall-Package Microsoft.jQuery.Unobtrusive.Validation 
Uninstall-Package jQuery.Validation
Uninstall-Package Bootstrap
Uninstall-Package jQuery
Uninstall-Package Respond
Uninstall-Package Modernizr

The Script folder is now significantly cleaned up, only containing the files that are generated as output from the gulp build.

Referencing Scripts and Styles

Next, we need to change the way we are referencing the script and css files. In Views\Shared_Layout.cshtml change the following in the head tag

@Styles.Render("~/Content/css")
@Scripts.Render("~/bundles/modernizr")

to

<link rel="stylesheet" href="~/Content/dist/css/app.min.css">
<script src="~/Scripts/modernizer-min.js"></script>

At the bottom of the page, change the following

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

to

<script src="~/Scripts/jquery-bundle.min.js"></script>
<script src="~/Scripts/bootstrap-bundle.min.js"></script>

And finally, remove any reference to the jquery validation bundle since we included those scripts in the jQuery bundle.

@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}

To complete the cleanup process, we can delete the App_Start/BundleConfig.cs file and remove the call to RegisterBundles from Global.asax.cs.

Ensuring Gulp runs before running the app

Currently, our solution requires the developer to manually call gulp from the command line to restore bower packages and re-generate the output files.

As we did in my previous blog post, we can use the Task Runner Explorer to ensure these tasks will be run anytime the developer builds the application in Visual Studio.

Open the Task Runner Explorer window (View –> Other Windows –> Task Runner Explorer). Select the default gulp task, right click, select Bindings –> Before Build. Now the default gulp task will run every time the application is built in Visual Studio. This will ensure that the output files are always up to date for the developer.

Conclusion

We now have a fully working application that uses Bower to reference client side libraries and Gulp to define and generate bundles. By using Bower, we get access to a larger and more up-to-date library of client side packages than we did with NuGet. Using Gulp, we have a lot more flexibility in how we define bundles than we did with the ASP.NET Bundling / Minification system.

This approach is more consistent with the way the rest of the world is building web applications today and should make for an easier transition into the next version of ASP.NET.