Grunt JS tutorial from Beginner to Ninja

Sometimes you find yourself doing the same tasks again and again, especially during web development. It is time to automate repetitive tasks and use that time in more creative activities. This is where Grunt comes in. Grunt is a popular task runner that runs on NodeJS. It can minify CSS/JavaScript, run linting tools (JSHint, JSlint, CSSlint), deploy to server, and run test cases when you change a file to name a few. All the information I found about Grunt and similar Javascript test runners were too verbose and not very helpful to get started quickly. So, I decided to make this tutorial.

Beginner: Grunt.js 101

Grunt.js is a Javascript task runner. At its bare core it does file manipulation (mkdir, reads, write, copy), print messages and helper methods to organize and configure multiple tasks. It takes care of differences among Operating Systems for you. However, the real power comes in with the number of available plugins ready to use. Usually named grunt-contrib-*. Let’s start from scratch!

grunt# A valid Gruntfile could not be found. Please see the getting started guide for more information on how to configure grunt: http://gruntjs.com/getting-started# Fatal error: Unable to find Gruntfile.

If you run grunt again, you will see a message. The default task is run when nothing else it is specified. We are going to create a 2nd task called ‘hello’ and it is going to accept a parameter that we can pass along with the task name separated with a colon. As follows: grunt hello:adrian. We can handle errors using grunt.warn. Every time a grunt.warn is found the task will stop executing, and it will give its warning message.. You can override using --force. Try all this commands and noticed the different effects: grunt, grunt hello, grunt hello --force, grunt hello:adrian.

taskName: required to register the task and it allows the task to be e executed with grunt taskName or called by other grunt task.

taskList: array of taskNames to be executed, in the order specified, when the taskName is called. E.g.: grunt.registerTask('concatAll', ['concat:templates', 'concat:javascripts', 'concat:stylesheets']);

Grunt Errors and Warnings

grunt.fail.warn(error [, errorcode]): prints to STDOUT a message and abort grunt executions. It can be override using --force and it can show the stack trace if --stack is given. e.g. grunt taskName --force --stack.

grunt.fail.fatal(error [, errorcode]): similar to warn, displays message to STDOUT and terminate Grunt. Cannot be --forceed and it emits a beep unless --no-color parameter is passed. It also accepts --stack. E.g. grunt taskName --no-color --stack.

Example: Forex and grunt multiple async calls handling

The idea is get conversion rates from a base currency (e.g. USD) to a target currency (e.g. EUR). We are using a registerMultiTask, so the taskName ‘currency’ matches its property in the config.init. Notice that we can has additional arbitrary data such as endpoint URL.

Async calls can be a little tricky in Javascript. We are going to do multiple HTTP request. Since http.get is async Grunt will finish the task before even receiving any response. this.async() solves the issue, we just need to call it when we are done.

Reference 2: Grunt Files and logs

Grunt logs

All them stars with the prefix grunt.log and accepts a msg which is displayed to STDOUT (usually the screen). Here are the differences between them:

writeln([msg]), write(msg) and subhead(msg): writes message to STDOUT. grunt.log.writeln will do the same as grunt.log.write but without trailing newline. subhead(msg) will print the message in bold and proceeded by a newline and a trailing newline as well.

The following methods adds a “>>” before the message in the screen which could be of different colors depending on the method:

grunt.log.error([msg]): print message prefixed with a RED “>>”.

grunt.log.ok([msg]): print message prefixed with a GREEN “>>”.

Grunt files

Files

All has an optional attributes options that could be encoding among others.

grunt.file.expand([options, ] patterns): returns an array with all the files matching a pattern. It can also accept and array of patterns. Preceding a patter with ! will negate them. E.g. ['**/*.js', !**/*spec.js] => get all javascript (including subdirectories) but NOT the ones that ends with spec.js.

Example 2: Gruntfile for files manipulation

GruntJS comes with built-in functions for basic file system handling. To see the function in action. Create four directories: stylesheets, javascripts, templates and put files on first three. The idea is to concatenate all the files into one index.html and placed it a newly created public folder.

Here’s the grunt file that will copy and concatenate all the files for us:

this.name: this is the name of the task. E.g. grunt hello, then this.name === 'name'.

this.args: returns an array with the parameters. E.g. grunt hello:crazy:world, then this.args will return ['crazy', 'world'].

this.options([defaultsObj]): it gets options values from the config.init, optionally you can also pass an object containing the default values. Notice in the example below that even though console.log has a this.options({gzip: true}) it gets override by the options parameters. If not one it is specified in the config.init then it will use the default gzip: true.

this.target: name of the target current target. If you call it grunt multiTaskName, it will run like multiple tasks calling each target one at a time. this.target will be equal to target1 and then target2.

this.files: return a (single) array that has all the properties for the current target. Take a look the the output above.

this.filesSrc: it expands files and paths against src and return an array with them.

In the next blog post, we will continue the tutorial with using GruntJS in a web application, making your own plugins and a comparison between other task runners tools such as Gulp, Gulp, Brunch, Rake::Pipeline and Broccoli.

Adrian Mejia is a full-stack web developer located in Boston.
Currently working at Cisco as a Software Engineer.
Adrian enjoys writing posts about programming and technology.
Also, he likes to travel ✈️ and biking 🚴‍. Find out more here.