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 ofSASS
toCSS
autoprefixer
to add vendor prefixes toCSS
watch
to watch for changes made toSASS
and then runsass
andautoprefixer
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
This opens the 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.