Friday, April 19, 2013

Kick Ass Cross-Platform Workflow With Grunt

My apologies for the vulgar title, but Grunt has me pretty excited these Days.  I work on a Mac at home and on a Windows machine at work, this means that cross platform tools are extremely important to my workflow. I hate having to use CodeKit at home and Scout at work to compile my .scss files.  Additionally tying a project to a platform specific tool means that its that much harder for others to get involved. Lately I've become a bit of a GruntJS fanboy, not only does it
meet my cross platform needs, but it cuts down the number of tools I need to use because it does so many things.
The watch plugin has made my workflow even better by allowing me to run Grunt tasks when files in my project are added, changed, or deleted. With watch whenever a .less or .scss file gets updated Grunt will see the change and automatically compile the file for me.  This post will walk you through installing watch and a compiler plugin and addresses a few of the gotcha's I ran into along the way. 

Install Grunt

Duh. Installing Grunt is out of the scope of this post but  the Grunt Homepage does an excellent job of walking you through the intstall (here's a hint you'll need to install Node too).  It's important to note here that version 0.4.0 and newer of Grunt works much differently than prior versions, and that the examples in this post will only work with version 0.4.0 or newer of Grunt.

Install Compiler Plugin

Once you have Grunt installed the next step is to get a compiler installed (since my current project is using Twitter Bootstrap I'm going to walk through setting up the LESS compiler in this post, however the process is the same for any other compiler).
To find the plugin you need the Grunt plugin page has a list of plugin's available for Grunt.  Clicking on a plugin will send you to the plugin's NPM page where you'll find documentation on the plugin and the command needed to install the plugin.

A quick not on plugins.  There are two type's of plugins "contrib" and "non-contrib".  The "contrib" plugins are official plugins and are maintained by the Grunt team.  I always prefer to use the "contrib" plugins whenever I can since they are generally better maintained and supported.
To install the Grunt LESS plugin:
  • open the terminal and navigate to the project's root directory in a terminal
  • type in npm install grunt-contrib-less --save-dev in the command line
  • hit enter
It really is that simple. The --save-dev command will ensure that the plugin, and its version, are added to your package.json file.
If you are not using a package.json file I strongly suggest you start. Using a package.json file means that you don't have to add your node_modules code to your distributions, users simply have to type in npm install in a terminal to install the dependencies.

Install The Watch Plugin

The next step is simple, install the watch plugin.  While still in your project's root directory in the terminal type in:
npm install grunt-contrib-watch --save-dev

Configure your Gruntfile

Before you go any further read the "Configuring Tasks" section of the Grunt website. This will save you a lot of time in the long run, I promise. Many plugins will assume that you know how to configure tasks and will leave these details out of their documentation.
A few points of frustration that I've run into that are covered in "Configuring Tasks" are:
  • Specifying the task name to configure. Typically the task name is the same as your plugin name.
  • Specifying source and destination files.  This is almost always left out of documentation and is almost always extremely important since a lot of plugins deal with manipulating files. 
  • Targets and how to use them (some plugins require targets and some do not, the bad news is that it's usually poorly documented whether a plugin uses them or not).
Configure the Compiler
Now that I've gotten that out of the way, lets walk through setting up the LESS compiler in the Gruntfile. In order to add a LESS task to the Gruntfile add a property named less to the object that is passed into the grunt.initConfig() function.  Since the LESS plugin requires targets (it will fail without them) I am going to specify a development target in the configuration.
A quick note on targets: if no target is specified when register or running a task ALL targets for a task will be executed.
The development target in this example only uses properties: src and dest. These properties are used to tell the LESS task where to find the .less files and where to put the compiled .css file. The syntax /**.*.less tells Grunt to get all files with a .less extension in the less directory and all of its sub-directories.


    less: {

        development : {

            src : [ 'less/**/*.less' ],

            dest : 'css/compiled-better.css'




Configure Watch
The watch plugin does exactly what it's name implies, watches a directory for changes.  Since this plugin does not require the use of targets I'll skip them in this example and will put properties directly under the watch task. At a minimum watch needs two properties: files and tasks. The files property tells watch what files to look for changes for, and tasks property tells watch what tasks to run when a change is detected.
The example below is telling watch to listen for any changes to files with a.less extension in the less folder and any of its sub-folders and to run the less task when a change is detected.
watch: {

    files : [ 'less/**/*.less' ],

    tasks : [ 'less' ]

Register Tasks
The last step in configuring your Gruntfile is to register the less and watch tasks. To do this the following lines are added to the Gruntfile immediately after the grunt.initConfig() call:




Run the Watch Task

The last step is to run the watch task. In a terminal window once again navigate to the root directory of the project and enter:
grunt watch
Grunt will run the watch task which will then tell you that its "Waiting...".  When a change is detected Grunt will spring into action and you'll see the normal Grunt messages start to appear in the console. When the tasks are complete watch will go back to the Waiting... status.

Fork me on GitHub