2013-12-30

Grunt your ASP.net builds

Visual Studio 2013 has made great strides at being a great tool for web development. When combined with the webessentials package it is actually a very good tool, maybe the best tool out there. However it has one shortcoming in my mind and that is the poor support for third party tools. It is difficult to track the latest trends in web development when you’re working on a yearly release cycle, as Visual Studio does. This is somewhat ameliorated by the rapid release of plugins and extensions. Still I think think that the real innovative development is happening in the nodejs community.

One of the things to come out of the nodejs community is the really amazing build tool grunt. If you haven’t seen grunt then it is worth watching my mentor, Dave Mosher’s video on automating front end workflows with grunt. In that video he takes us through a number of optimizations of CSS and JavaScript using grunt. Many of these optimizations are not things which exist yet in any Visual Studio tooling that I’ve seen. Even in cases where the tooling does exist I find that it is being incorrectly executed.

Ideally your development environment mirrors your production environment exactly. However time and financial constraints make that largely impossible. If production is a hundred servers there is just no reasonable way to get a hundred servers for each developer to work on all the time. Unless your company has pots of moneyMoney_in_Flower_Pot

In which case, give me a call, I would be happy to spend it on insane projects for you. Perhaps you need a website which runs on a full stack of poutine instead of a full stack of JavaScript”¦

Visual Studio falls down because when you F5 a project the environment into which you’re placed is not the same as the package which is generated by an upload to your hosting provider. It is close but there are some differences such as

These actions can be breaking changes which will not be apparent in the development environment. For instance changing the names of function arguments, as is common in minification, tends to break AngularJS’ injection.

Thus I actually suggest that you use Grunt instead of the built in minification in ASP.net. Let’s see how to do exactly that.

The first thing you’ll need is a copy of nodejs. This can simply be the copy which is installed on all your developer workstations and build server or it can be a version checked into source control(check in vs. install is a bit of a holy war and I won’t get into it in this post). If you’re going to check a copy of node in then you might want to look at minimizing the footprint of what you check in. It is very easy with node to install a lot of packages you don’t actually need. To isolate your packages to your current build then you can simply create a “node_modules” directory. npm, the package management system used by node, will recurse upwards to find a directory called node_modules and install to that directory.

Let’s assume that you have node installed in a tools directory at the root of your project, next to the solution file, for the purposes of this post. In there create an empty node_modules directory

Node and an empty node_modules directoryNode and an empty node_modules directory

Now that node is in place you can install grunt and any specific grunt tasks you need. I have a separate install of node on this machine which includes npm so I don’t need to put a copy in node_modules. I would like to have grunt in the modules directory, though.

npm install grunt

This will install a copy of grunt into the node_modules directory. There are a huge number of grunt tasks which might be of use to use in our project. I’m going to stick to a small handful for minifying JavaScript and base64 encoding images into data-uris.

npm install grunt-contrib-uglify npm install grunt-contrib-concat npm install grunt-data-uri

Now we will need a gruntfile to actually run the tasks. I played around a bit with where to put the gruntfile and I actually found that the most reliable location was next to the node executable. Because grunt and really node in general are more focused around convention over configuration we sometimes need to do things which seem like hacks. The basic gruntfile I created looked like

This is pretty basic but it does take all the javascript files in the project and combines them into one then minifies that file. It will also insert dataURIs into your css files for embedded images. A real project will need a more complete grunt file.

Now we need to run this grunt process as part of the build. As I mentioned this is a bit convoluted because of convention and also because we’re choosing to use a locally installed grunt and node instead of the global versions.

First we create a runGrunt.bat file. This is what visual studio will call as part of the post build.

Then tie this into the msbuild process. This can be done by editing the .csproj/.vsproj file or by adding it using Visual Studio

Screen Shot 2013-12-28 at 6.46.58 PM

The final step is to create a wrapper for grunt as we don’t have the globally registered grunt

Now when the build runs it will call out to grunt via node.One final thing to keep in mind is the output files from the grunt process are what is needed to be included in your html files instead of the source files. So you’ll want to include all.min.js instead of the variety of JavaScript files.

This opens up a world of advanced build tools which we wouldn’t have at our disposal otherwise. At publication there are 2 047 grunt plugins which do everything from zip files, to check JavaScript styles, to running JavaScript unit tests”¦ Writing your own tasks is also very easy(much easier than writing your own msbuild tasks). Setting up grunt in this fashion should work for automated builds as well.

Best of luck using node to improve your ASP.net builds!


comment: