Getting started with Grunt, SASS and Task Runner Explorer - Visual Studio

Getting started with Grunt, SASS and Task Runner Explorer - Visual Studio

Web Essentials opened the door to SASS in Visual Studio by giving us a way to compile without the need for Ruby. But now we have Task Runner Explorer, FTW!

Thank you Web Essentials

Web Essentials brought us SASS compilation using LibSass (well node-sass, which binds to LibSass). The big wins were; no dependency on Ruby and integrated compilation with save and build actions. But it wasn't without it's problems, sometimes I found compiles failed for no apparent reason and only a good old Visual Studio restart would sort it out. Also, In a multi developer environment using Web Essentials as a compiler isn't ideal as you have to check in generated CSS files normally ending up with unnecessary merge conflicts, which are a pain to resolve.

So what's next?

Mads Kristensen stated in a recent post that compiling is going from Web Essentials:

We’re getting rid of all the node.js based tooling in Web Essentials, such as LESS/Sass/CoffeeScript compilers, minification, JSHint/JSCS integration etc.

Ok, so what's coming to replace this? Well, the answer is existing tooling, in the shape of Grunt and Gulp. It makes perfect sense, why reinvent the wheel.

Task Runner Explorer

We can actually compile with Grunt today using the Task Runner Explorer extension. This lets you execute Grunt/Gulp tasks inside Visual Studio. I won't go into detail about this as Scott Hanselman's post covers this. So let's use Grunt to compile our SASS.

Compiling SASS with libsass, Autoprefixer and grunt

Note: It is assumed that Node.js is installed on your machine.

Goals

We are going to create a script to compile SASS, create a source map and add vendor prefixes with Autoprefixer. We will create two builds; one that will watch our SASS files for changes and recompile automatically and one that will just compile our SASS. The latter build task is for use on a build server.

Setting up the web project

At the root of the project, to create a package.json file run

npm init

Dependencies

To be able to compile to CSS, prefix, create a source map and watch for changes we need the following dependencies.

Grunt

Install Grunt's command line interface (CLI) globally.

npm install grunt-cli --save-dev

Grunt-sass

To compile SASS using Grunt, install grunt-sass plugin. Note this uses LibSass whereas grunt-contrib-sass requires Ruby.

npm install grunt-sass --save-dev

Grunt-autoprefixer

To add vendor prefixes to CSS properties, install grunt-autoprefixer.

npm install grunt-autoprefixer --save-dev

Grunt-contrib-watch

And finally, to watch for changes in SASS files install grunt-contrib-watch.

npm install grunt-contrib-watch --save-dev

By installing as a dev dependency a node_modules folder is created in the root of our project and each plugin is added to our package.json.

"devDependencies": {
    "grunt": "^0.4.5",
    "grunt-autoprefixer": "^1.0.1",
    "grunt-cli": "^0.1.13",
    "grunt-contrib-watch": "^0.6.1",
    "grunt-sass": "^0.16.1"
  }

This means when another developer checks out this project they can run npm install at the root of the project these dependencies will be installed.

Gruntfile.js

Here's what my gruntfile.js looks like.

module.exports = function (grunt) {
    'use strict';

    grunt.loadNpmTasks('grunt-sass');
    grunt.loadNpmTasks('grunt-autoprefixer');
    grunt.loadNpmTasks('grunt-contrib-watch');

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

        // Sass
        sass: {
            options: {
                sourceMap: true, // Create source map
                outputStyle: 'compressed' // Minify output
            },
            dist: {
                files: [
                  {
                      expand: true, // Recursive
                      cwd: "sass", // The startup directory
                      src: ["**/*.scss"], // Source files
                      dest: "stylesheets", // Destination
                      ext: ".css" // File extension 
                  }
                ]
            }
        },

        // Autoprefixer
        autoprefixer: {
            options: {
                browsers: ['last 2 versions'],
                map: true // Update source map (creates one if it can't find an existing map)
            },

            // Prefix all files
            multiple_files: {
                src: 'stylesheets/**/*.css'
            },
        },

        // Watch
        watch: {
            css: {
                files: ['sass/**/*.scss'],
                tasks: ['sass', 'autoprefixer'],
                options: {
                    spawn: false
                }
            }
        }
    });

    grunt.registerTask('dev', ['watch']);
    grunt.registerTask('prod', ['sass', 'autoprefixer']);
};

What's going on here

We have three tasks configured:

  • sass for compilation of SASS to CSS
  • autoprefixer to add vendor prefixes to CSS
  • watch to watch for changes made to SASS and then run sass and autoprefixer tasks

Then finally, two alias tasks are registered, dev and prod. prod is used on the build server and dev when developing.

Running tasks with Task Runner Explorer

Using Task Runner Explorer is very simple, by right clicking on gruntfile.js you get a option for Task Runner Explorer

gruntfile context menu

This opens the Task Runner Explorer.

Task Runner Explorer

On the left you a have a list of tasks and alias tasks. These can be run or bound to the build events on the right. This adds a reference to the top of gruntfile.js like so.

/// <vs SolutionOpened='default' />

And there we have it, with the dev alias task bound to SolutionOpened, when we open the project our grunt task to watch SASS files will run. This example is with SASS but this applies to LESS and TypeScript too. Web Essentials has been a great accelerator in making these tools usable in Visual Studio, but the future is looking bright with Task Runner Explorer.