What’s Grunt used for? Automating front-end and JavaScript workflow tasks. Refreshing the browser when you change a script. Minifying and concatenating. Running tests. Think rake
and guard
, if you’re coming from the Ruby world.
Enter Grunt by Example! A blow-by-blow tutorial. Just the way I like it. Let’s dive in.
The catch - Grunt configuration files can be fairly convoluted at first glance, usually due to the fact that developers add more and more steps to their workflow over time.
Grunt is just a task runner. Every unit of functionality that you would want is usually achieved with a separate npm package (a grunt “plugin”).
npm search grunt
to view literally every grunt plugin available.
Get the command line interface: npm install -g grunt-cli
Add the actual grunt task runner as a development dependency to your project (--save-dev
adds the package as a dependency to package.json
):
npm install --save-dev grunt
Let’s follow a process of gradual expansion.
Create an empty Gruntfile.js
file in your project root. This will contain all of your task configuration.
Let’s say that the first task we want to add to our workflow is to be able to concatenate several JavaScript files into one. Let’s say those files are all in the scripts/
sub-directory. We’d want to do this before deploying a website, for example.
Run npm install --save-dev grunt-contrib-concat
.
Let’s look at our Gruntfile configured with just this one task.
A quick preview of the result before we look at the code. After setting up our configuration object, running grunt concat
, grunt concat:dist
, or grunt build
in the shell in the project root will all do the same thing: concatenate all scripts in the scripts
sub-directory into a single script called main.js
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
|
You might still be confused about the notion of a “target.” More on that:
Let’s say there’s another set of files you want to concatenate. All script files in deploy/
should be concatenated into deploy.js
(in the project root). Let’s say we want that task to look like grunt concat:deploy
To achieve that, our concat task now looks like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
If you run grunt concat:dist,
all scripts in scripts/
will be concatenated into dist/main.js
. grunt concat:dist
runs the concat task with the config settings that are specified under dist
. grunt concat:deploy
runs the concat task with the config settings specified under deploy
. grunt concat
will run concat with both targets, separately. The parent-level options
setting specifies config settings that are shared by both targets. grunt build
is an alias for grunt concat
.
Now let’s tackle a common, yet relatively complicated task.
Let’s get Grunt to run certain tasks in response to changes in files. An extremely powerful and common use-case: reloading a static website when you change its HTML/CSS/JS. It’s fairly complicated and it’s usually something you just copy-and-paste. Here are the steps to setting this up using the watch
and connect
plugins, along with the connect-livereload
(a piece of Connect middleware that is not grunt-specific). For this example, let’s assume that your static website is located in the client/
folder.
Install the three plugins:
npm install --save-dev grunt-contrib-watch grunt-contrib-connect connect-livereload
Add this basic initialization code to the top of your Gruntfile:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
Add to your grunt.initConfig object the following two tasks configurations:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
|
Finally, we need to run the connect
and watch
tasks in sequence. To save ourselves from having to type in two shell commands, we can register a task alias:
1
|
|
Running grunt preview
will now run both tasks.
Some more grunt plugins to be aware about (contrib
plugins are officially maintained):
grunt-contrib-uglify
for minifying your JS files. It works similarly togrunt-contrib-concat
.grunt-contrib-jshint
for running JSHint.grunt contrib-coffee
for compiling CoffeeScript.grunt-contrib-sass
for compiling SASS.grunt-concurrent
for running tasks concurrently (instead of sequentially) - useful if you want to run multiple watch tasks concurrently.- If you have a series of tasks, the
watch
task must be run last.watch
is a task that never ends until you terminate it. Thus, Grunt won’t ever reach tasks that come afterwatch
.
- If you have a series of tasks, the
grunt-nodemon
for running nodemon with your node app. This runs your node app and reloads it when files change.grunt-simplemocha
for running mocha tests.grunt-open
for opening files and URLs.
Some tips:
- If your Gruntfile starts getting too unwieldy, you can break it up by using
grunt.file.readJSON()
. - Tired of copying and pasting
loadNpmTasks
? Try this:npm install --save-dev matchdep
- use
require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);
where you would otherwise list out all your multiplegrunt.loadNpmTask
calls.
For more advanced concepts, read the Grunt docs! They’re pretty good.