Converting your ASP.NET MVC5 application to use Bower, Grunt and Gulp

Last Updated: November 5, 2016 | Created: February 9, 2016

I changed over to Visual Studio 2015 (VS2015) a while ago, and one of the main reasons for moving was to use some of the new features for handling web packages. I also wanted to look at the preview of ASP.NET Core 1 to see how they used these features.

VS2015 contains as standard Bower, which is a web package manager used across the whole of the web development community. In addition VS2015 supports a web build automation system using Grunt and/or Glup. These are great tools for a number of reasons that I won’t cover now, but at the end of this article I list some of the advantages I found when I swapped over – and I think they are really worth having!

Note: While this article talks about VS2015 it is also applicable to VS2013 as the same Bower, Grunt and Gulp features are available for VS2013 via add-on extensions. If you have VS2013 then read this article by Scott Hanselman on what packages you need.

I personally needed the power of these tools on an ASP.NET MVC5 e-commerce project which is nearing completion and we are doing fairly drastic things to the design and JavaScript front-end. I didn’t want to upgrade to ASP.NET Core 1 this late in the project, especially as ASP.NET Core 1 is very different to MVC5 and not released yet. I therefore set about harnessing these tools in an existing project, with the aim of following the ASP.NET CORE 1 style to make upgrading later as simple as possible.

NOTE: There is a sample application on GitHub to go with this article. In this application I have converted an existing ASP.NET MVC5 project away from using the normal NuGet/BundleConfig to using Bower and Grunt for handling web packages. It is open-source so please have a look and see if it is useful.

This sample application also contains a library called BundlerForBower, B4B for short, which takes over the role of MVC’s BundleConfig. I mention the use of B4B towards the end of this article, but I have also written a more detailed article about B4B called Introduction to BundlerForBower for ASP.NET MVC5.

UPDATE: BundlerForBower is now available on NuGet

BundlerForBower is available on NuGet – see https://www.nuget.org/packages/Bundler4Bower/ 

Swapping from NuGet to Bower

If you create a brand new ASP.NET CORE 1/MVC solution in VS2015 then the MVC project looks very different to the current ASP.NET MVC5 structure. This is because the whole structure has been reconfigured to be more applicable to modern web development (see the ASP.NET CORE 1 overview article for more information).

We are interested in how it handles the web packages because if we are going to adopt Bower etc. we want to use the same approach as the new ASP.NET structure so that any future upgrades will be simpler. There are many changes in ASP.NET CORE 1 but the key web package changes are:

  1. It automatically uses Bower, not NuGet, to load web packages like JQuery, Bootstrap etc.
  2. It includes a ‘Dependencies’ virtual folder that contains:
    1. A Bower folder that containing our web libraries, e.g. JQuery
    2. An npm folder that contains our build automation tools, e.g. Gulp and plugins.
  3. It doesn’t have an App_Start/BundleConfig.cs as Gulp does the bundling/minification.

So let us now change our existing MVC5 project to match this approach.

1. Use Bower, not NuGet for web package management

If you have an existing MVC5 application you won’t see the Manage Bower Packages option in any of your menus. It needs a file to unlock the feature. Therefore the very first step is to do is create a bower.json file to your MVC5 project, by right clicking the MVC project folder in the solution explorer and select: Add>New Item and then find the “Bower Configuration file” in the list of templates, e.g.

CreateBowerFile

This will create the file bower.json and activate bower. The bower.json is the file that Bower writes to when you load a packages. This is useful for package restore, i.e. restoring the bower files if haven’t already got them loaded. As you don’t normally include the web packages in your source control then you need this if loading the application into a different development environment.

You don’t have to, but I also set up a .bowerrc file. This is the Bower configuration file and contains things like what directory where the packages will be written to. I added the following in the .bowerrc file:

{
  "directory": "lib"
}

which set the top-level directory ‘lib’ as the place that bower will place the libraries it loads. If your don’t do this then I think (I haven’t checked and the docs don’t say) the default is the top-level directory ‘components’, based on this stackoverflow question.

Once you have these files you when you right click the MVC project folder in the solution explorer you should see a new option: Manage Bower Packages (Note: I have found that this command doesn’t immediately appear once you have added the files. Restarting the Visual Studio and reloading the solution fixes this).

Selecting the Manage Bower Packages function brings up a screen (shown below) which is very like the new V3 NuGet screen. However in this case it is accessing the bower.io API to allow you to browse the packages available via Bower.

ManageBowerPackages

BE WARNED: I have found a few of issues in the current Bower interface:

  1. I used the interface to try and find a package called ‘fontspy’ but it could not find it, although Bower’s online search said it was there. However if I went into the bower.json file and typed “fontspy” intellisence knew about it and gave me the version. I then right-clicked the bower.json file and selected Restore Packages and fontspy loaded ok. The interface never showed that fontspy was loaded.
  2. I found Underscore using the interface, but it only gave version 1.5.2, which is an older version. If I went into bower.json and typed “underscore” it found version 1.8.3. I used that version by using Restore Packages to load it (see point 1).
  3. The interface says ‘No dependencies’ for packages that do actually have dependences, which is a bit confusing. Clicking the ‘project URL’ and looking at a file called ‘bower.json’ will show you the actual dependencies. However, even if the screen doesn’t show the dependencies bower will load any dependant packages if required.

If you select and install a package two things happen:

  1. The bower.json file is updated with the loaded package(s).
  2. The package and any of its dependant packages are loaded to the directory pointed to by .bowerrc.

Here is the content of bower.json after JQuery, BootStrap and Microsoft.jQuery.Unobtrusive.Validation has been loaded.

{
  "name": "ASP.NET",
  "private": true,
  "dependencies": {
    "jquery": "2.1.4",
    "bootstrap": "3.3.5",
    "Microsoft.jQuery.Unobtrusive.Validation": "~3.2.3"
  }
}

By default the loaded packages are not included in the project, which is good as we don’t want them to be sent to the web site, or store in source control. However this means to see them we need to turn on the ‘Show all Files’ option in VS’s Solution Explorer, and after installing a new package you will need to ‘Refresh’ the Solution Explorer view. Here is a view of the loaded packages directories with bootstrap opened up.

Note: You won’t see the lib directory in the sample application as they are not included in source control. If you download the application you need to right-click on the ‘bower.js’ file and click Restore Packages. That will download the packages and populate the lib directory.

BowerLibShowingBootStrap

Note: you will see that package jquery.validation is loaded even though it does not appear in the bower.json file. This is because Microsoft.jQuery.Unobtrusive.Validation’s own bower.json files says it dependents on jquery.validation so Bower automatically loads that if it is not already there.

2. Remove web packages loaded by NuGet

Tip: It took me a bit of searching to find the equivalent web packages in Bower. JQuery was easy but some of the packages have difference versions with very similar names. It’s worth cross-referencing the files that NuGet loaded with the bower equivalent before you uninstall the NuGet versions.

Previously you used NuGet to load your web packages, but when changing to Bower I really recommend your remove those packages from NuGet. This ensures that you won’t confused between the two packages, and more importantly if Bower updates its packages there isn’t a chance that you are using an older package loaded by NuGet. I also find it nice to not have a long list of JavaScript files under the ‘Script’ directory – now I can put my JavaScript files in there and it’s not confusing.

To Uninstall NuGet loaded packages then simply open NuGet and select the ‘Installed’ tab, then ‘Uninstall’ the packages that Bower is now handling. Note that your current views and layouts will stop working, which leads me on to the next section.

Swapping from BundleConfig to Grunt/Gulp

In ASP.NET MVC5 CSS and JavaScript files were normally delivered by App_Start/BundleConfig.cs which also looked after bundling and minification in a released application. Also, if you required compiling of Less, Sass, TypeScript, CoffeeScript, templates, image spriting etc. you may have used Web Essentials.

The new way is to use build automation tools like Grunt or Glup to achieve the same thing. These tools have a large base of what are called ‘Plugins’ which include all the features of Web Essential and much more.

You have a choice of using either Grunt or Gulp, or both. They both do the same thing but in slightly different ways. I ended up using Grunt for no more reason than I found some useful commands in Grunt format. Gulp is the standard automation tool if you create a new ASP.NET CORE 1 project, but as I said either will do the job.

Note: If you want to use Gulp instead of Grunt then I recommend this ASP.NET CORE 1 documentation on Gulp. If you want an example Gulp file then create an ASP.NET CORE 1/MVC project and look at the gulpfile.js that it produces.

1. Installing Grunt

The build automation tools need to be loaded, and for that we use NPM. NPM is a library system that makes it easy to share code. Grunt (and Gulp), plus there extensions, called plugins, can be loaded by NPM into your application.

First you need to add a NPM configuration file to the project in a similar way you did for the Bower configuration file, i.e. right clicking the MVC project folder in the solution explorer and select: Add>New Item and then find the “NPM Configuration file” in the list of templates.

This adds a file called package.json and opens you in that file. Now you manually add the Plugins you need. Thankfully you get great intellisence help with package names and their versions, so it’s not too hard. What you put in depends on what you want to do, but here is my file as a start:

{
  "version": "1.0.0",
  "name": "ASP.NET",
  "private": true,
  "devDependencies": {
    "grunt": "0.4.5",
    "grunt-contrib-clean": "0.6.0",
    "grunt-contrib-cssmin": "0.14.0",
    "grunt-contrib-jshint": "0.11.0",
    "grunt-contrib-concat": "0.5.1",
    "grunt-contrib-uglify": "0.8.0",
    "grunt-contrib-watch": "0.6.1",
    "grunt-contrib-copy": "0.8.2"
  }
}

Here is link to the reference article that I got this from. The only extra command I added was grunt-contrib-copy, which I needed for copying the bootstrap font files.

Once you have filled or changed the package.json file you should right-click the file and click Restore Packages. This will cause npm to look for those packages and load them. If you do a Refresh in the VS Solution Explorer you should find the package/plugins in the ‘node_modules’ directory.

NOTE: Be warned. If you have made a mistake in the file you won’t get any kind of error feedback when you run Restore Packages – it still says ‘Installing packages complete’. However if the files don’t seem to update look at the Output Window>Bower/NPM.

2. Define your Grunt tasks

You define the tasks you want to call in the Grunt configuration file, using the normal right-click the project and select Add > New Item. Select the Grunt Configuration file option, leave the default name, gruntfile.js, and click the Add button.

There are lots of ways of doing this and not really the space to explain all the possibilities, but I do give some links later. To give you a general introduction I have listed a very cut-down gruntfile.js which only concatenates and minifies the CSS files.

module.exports = function (grunt) {

    // Project configuration.
    grunt.initConfig({
        concat: {
            css: {
                src: ['lib/bootstrap/dist/css/bootstrap.css', 'Content/Site.css'],
                dest: 'css/styles.css'
            },
        },

        cssmin: {
            css: {
                src: 'css/styles.css',
                dest: 'css/styles.min.css'
            }
        }
    });

    // Load the plugin that provides the tasks we need
    grunt.loadNpmTasks('grunt-contrib-concat');
    grunt.loadNpmTasks('grunt-contrib-cssmin');

    // Default task(s).
    grunt.registerTask('default', []);

    // Build task(s).
    grunt.registerTask('build:css', ['concat:css', 'cssmin:css']);
};

In the example above I listed the files I wanted to concatenate from the packages that Bower loaded earlier. The simplest way to do this is compare the names of the files you loaded via your existing BundleConfig class and look for the same name in the lib directory. The files you want are often in a directory called ‘dist’, but some packages like Microsoft.jQuery.Unobtrusive.Validation don’t follow that rule.

With the gruntfile.js set to the code shown above up then if you can open the Task Runner Explorer window (try View > Other Windows > Task Runner Explorer) then you should see something like this:

TaskRunnerWindowYou can see the concat:css and cssmin:css tasks, plus the build:css task which executes concat:css followed by cssmin:css. The result of running concat:css is you will find a file called ‘styles.css’ which is a combination (bundle) of the bootstrap.css file followed by the Site.css file. If you run cssmin:css then a second file called ‘styles.min.css’ will appear which is the minified version of ‘styles.css’.

You run commands by right-clicking on a command and selecting Run. Feedback is pretty good, with the output of the task shown to the right of the command part of the window.

There is plenty more you can do but the example above gives you the basic idea. I would refer you to these useful articles for more information.

Note: different gruntfile in the sample application

You will find that in the sample application I use a different approach to specifying the files that are in each bundle, because I want another feature, BundlerForBower (explained later), to also have access to the list of bundles and their files. Therefore I place the array of files in a json file which I store in the App_Data directory. You can see the complete version of my gruntfile.js here and the json file it reads here.

3. Binding actions to Visual Studio Events

TaskRunnerWindowYou can bind any of the actions in the GruntFile to a Visual Studio events: Before Build, After Build, Clean and Project Open. You do this by right-clicking on a command you want to link and selecting Bindings. Below is an example taken from the ASP.NET grunt documentation, where you bind the watch feature to Project Open.

These bindings are stored as a specially formatted comment at the top of the gruntfile.json.

4. Delivering CSS/JavaScript to the Brower

The new ASP.NET CORE 1/MVC6 project does not use BundleConfig.cs to combine and minify CSS and JavaScript. It uses some new razor <environment names=”Development”> feature to choose between individual files or the concatenated & minified files produced by the Grunt/Glup task. It seems that the approach is to include all the individual files in in Development mode. In Production/Release it includes the concatenated and minified file with a cache buster suffix added using the new asp-append-version tag inside a HTML <script>. The asp-append-version tag generates unique hash based on the file content (see this helpful stackoverflow answer explanation).

In an existing MVC5 project we don’t have the new <environment> or the asp-append-version tag so we need to find an alternative. There are two ways to do this:

a. Use BundleConfig.cs to deliver the concatenated files

One of the reasons I chose the Grunt approach is it created both a concatenated, non-minified file and the minified file for both CSS and JavaScript. This means I could create a bundle inside BundleConfig.cs that looked like this:

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

In development mode it would deliver one large CSS file, styles.css, which isn’t minified. In release mode it would use the styles.min.css file with a cache buster suffix, i.e. a string that changes when the file changes which makes sure an older, browser-cached version isn’t used.

b. Using the library BundlerForBower (B4B)

The above solution works but throws up loads of issues when debugging of JavaScript. As the whole point of using Bower etc. is because you have a lot of front-end code to debug the first solution is less than idea.

I therefore built a specialised version of the MVC5 BundleConfig, called BundlerForBower or B4B for short, which is specifically written to work closely with Bower, Grunt and Gulp. It also follows some of the design styles used in the current ASP.NET Core 1 applications.

B4B consists of some code that performs a similar role at MVC’s BundleConfig class plus two extension methods that are very similar Html helper methods to MVC5’s `Styles` and `Scripts` classes to deliver bundles in a view. It is therefore quite easy to convert an existing MVC5 views over to B4B by making the following replacements:

@Styles.Render(“~/Content/mainCss”) is replaced by @Html.HtmlCssCached(“mainCss”) and @Scripts.Render(“~/bundles/javaScriptBundle”) is replaced by @Html.HtmlScriptsCached(“javaScriptBundle”)

As well as bundles that deliver groups of files from within your application ASP.NET Core 1 has a set of tags for delivering files from a Content Delivery Network (CDN), with fallback capabilities. I have reproduced this feature in B4B as it is so useful. Public CDNs are available across the world for many of the standard libraries like JQuery and Bootstrap, and can be faster than local delivery especially if the user has already loaded the standard library when using another web site.

NOTE: I have written an article called ‘Introduction to BundlerForBower for ASP.NET MVC5‘ which describes B4B in detail. B4B is an open-source (MIT) project and is included in the sample application.

My reflections on using Bower and Grunt

I think that Bower and Grunt/Gulp are a really good step up from the old NuGet, BundleConfig and Web Essentials. I spotted a few issues that I have noted in this article, but they were easy to get round and are likely to be fixed soon. The difference was immediately apparent as I was now much more in control on my web packages. It solved three problems that I had with the NuGet, BundleConfig and Web Essential’s approach which are worth listing, as they point out some of the gains this new approach brings.

1. Better coverage of packages

I use a JavaScript package called JQuery-FontSpy, which isn’t in NuGet. Previously I had to copy the files I needed by hand from GitHub. However JQuery-FontSpy is, of course, available in Bower. This is typical of more specialised packages and one of the reasons why switching to Bower is so useful.

2. More up to date packages

I am using handlebars.js templates in my web application to dynamically create complex HTML at runtime via JavaScript. While a handlebars package is available via Nuget it is at version 3.0.0 which isn’t the latest and misses a feature I would have liked to use. However the Bower version is at version 4.0.3, which is the latest, and because Bower is the main way of releasing packages like this then it will always have the latest.

This reminds us that web packages only get into NuGet because someone takes the trouble to transfer it to NuGet, and they may not feel the need to keep it updated. Whereas Bower is the primary tool for deploying web packages so the latest and greatest of a package will be available.

3. Bower libraries can contain more information

The Nuget version of libraries like BootStrap and JQuery contain just the final .js and .min.js versions of the file. However if you load these libraries via Bower you find they have a directories that contain the various parts of the library.

This is very helpful for me as I change my e-commerce site from using BootStrap to the final CSS design scheme. While I don’t want much of BootStrap’s CSS I would like to use some of its modules, like Modal, to save me reinventing that code. If I was using NuGet I would have had to go the bootstrap site and make a custom download, which I would have to redo if any new version came out. However with Bower I simply installed the bootstrap package and add the individual modal JavaScript and CSS, via less, files I need from the bootstrap\js and bootstrap\less directories to my gruntfile.js build script. If I want to add another module, like tooltip, I just include those files in my build script too. This is a much nicer approach than a custom download.

Even more web tools…

I should mention an alternative/adjunct to Bower etc. in the form of ‘Node.js tools for Visual Studio’ extension for VS. This has some extra features over the plain Bower, Grunt/Glup such as JavaScript debugging and profiling. It is definitely worth a look.

I used Node.js as a separate application some years ago when developing a Single Page Application and Node.js was great, if a bit daunting to learn coming from a VS background. However it seems like ASP.NET CORE 1 is going to use Bower, Grunt/Gulp as standard so I decided to start with that for now. You can use both the build in Bower and the Node.js extension together, so maybe I might progress to the Node.js extension in the future.

Conclusion

I have really appreciated the new Bower, Grunt/Gulp tools. They have made changing my e-commerce web site over to a proper design much easier. I also find the build tools are more comprehensive, yet allow me to just run one commend, ‘build’, to make sure everything is up to date.

I did have to create BundlerForBower (B4B) for helping with the process (see separate article on this). B4B means I have one file, BowerBundles.json, to update and everything ripples through the system. I also like the Unit Test checking that B4B contains as it stops me forgetting something before I release to production.

Please feel free to take a copy of the sample application and play with it. Hopefully this article and the sample will help you convert to Bower and be ready for ASP.NET Core 1.

Happy coding!