Integrating Grunt into ASP.NET projects

Dzulqarnain Nasir 11.06.2014 05.38.00

Firstly, you’ll need to install the following NuGet packages into your project:

This will also install the following NuGet packages:

The NuGet packages above comes with the binary files required to run Grunt, Bower, and NodeJS Package Manager (NPM). So you can use them even if you don’t already have them installed on your machine.

Then you’ll need to add your gruntfile.js. You can find lots of examples online, but for simplicity’s sake, here’s what mine looks like for the most part.

module.exports = function(grunt) {

    grunt.initConfig({

        // Variables

        productionPath: 'public',

        pkg: grunt.file.readJSON('package.json'),

        // Tasks

        less: {
            development: {
                options: {
                    compress: false,
                    yuicompress: false,
                    optimization: 0,
                    sourceMap: true,
                    sourceMapFilename: "Content/site/site.css.map",
                    sourceMapBasepath: "Content/site/"
                },
                files: {
                    'Content/site/site.css': 'Content/site/site.less'
                }
            },
            production: {
                options: {
                    compress: true,
                    yuicompress: true,
                    optimization: 2,
                    sourceMap: true,
                    sourceMapFilename: "<%= productionPath %>/site.css.map",
                    sourceMapBasepath: "<%= productionPath %>/"
                },
                files: {
                    '<%= productionPath %>/site.css': 'Content/site/site.less'
                }
            }
        },

        uglify: {
            production: {
                options: {
                    sourceMap: true,
                    compress: {
                        drop_console: true
                    }
                },
                files: {
                    '<%= productionPath %>/site.js': ['Scripts/site/**/*.js']
                }
            }
        },

        watch: {
            options: {
                livereload: true
            },
            less: { 
                files: ['Content/site/**/*.less'],
                tasks: ['less']
            },
            js: { 
                files: ['Scripts/site/**/*.js'],
                tasks: ['uglify']
            }
        }
    });

    // Set up

    grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.loadNpmTasks('grunt-contrib-less');
    grunt.loadNpmTasks('grunt-contrib-watch');

    grunt.registerTask('default', ['uglify', 'less']);
}

Next, you'll need to add two files, called package.json and bower.json that we'll be using to store information about your project dependencies. package.json is used to store information about your Grunt dependencies, while bower.json is used to store information about packages your project will use.

Here's what my package.json file looks like:

{
  "name": "myAwesomeProject",
  "version": "0.1.0",
  "description": "",
  "scripts": {},
  "author": "Dzulqarnain Nasir",
  "license": "",
  "devDependencies": {
    "grunt": "~0.4.5",
    "grunt-contrib-uglify": "~0.4.0",
    "grunt-contrib-watch": "~0.6.1",
    "grunt-contrib-less": "~0.11.1"
  }
}

And here's what my bower.json file looks like:

{
  "name": "myAwesomeProject",
  "version": "0.1.0",
  "dependencies": {
    "angular": "1.2.6",
    "angular-route": "1.2.6"
  }
}

If at a later stage you need to include additional packages, you can do so using npm install [package-name] --save-dev for NPM and bower install [package-name] --save for Bower, which will download the specified packages and register them into the appropriate package information file.

Finally, you'll need to update your CSPROJ file with the following lines:

<Target Name="BowerInstall" BeforeTargets="PrepareForBuild">
<Exec Command=".bin\bower install" />
</Target>
<Target Name="NpmBuild" BeforeTargets="GruntBuild">
<Exec Command=".bin\npm install" />
</Target>
<Target Name="GruntBuild" AfterTargets="BowerInstall">
<Exec Command=".bin\grunt" />
</Target>

You're basically telling Visual Studio to fetch and install all the necessary dependencies, and execute Grunt before building the project. But how does it know what your project dependencies are? Remember those dependency information files you created earlier? The information stored in these files will be used to determine what packages need to be included in your project.

One cool thing about integrating Grunt into your build process is that, if for some reason Grunt fails, like if your code contain syntax errors or your unit tests fail, your whole project build process will also fail, and you'll be able to see what went wrong in the Error Console. Pretty useful feature, if you ask me.

You can also manually run Grunt if you want to execute your Grunt tasks but don't feel like rebuilding your entire project, simply by executing .bin\grunt (or simply grunt if you already have Grunt installed on your machine) in the NuGet Package Manager Console, since it's essentially just Powershell.

Grunt is a powerful tool. While I'm only using it for compiling and minifying files, there's a whole bunch of other stuff you can do with it, and implementing it as part of your workflow will help improve productivity by cutting down the time you spend on common repetitive tasks.

Happy coding!

Wassalam