Home Web-development Grunt Cookbook

Grunt Cookbook

By Jurie-Jan Botha
books-svg-icon Book
Subscription
$10 p/m for first 3 months. $15.99 p/m after that. Cancel Anytime!
What do you get with a Packt Subscription?
This book & 7000+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook + Subscription?
Download this book in EPUB and PDF formats, plus a monthly download credit
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook?
Download this book in EPUB and PDF formats
Access this title in our online reader
DRM FREE - Read whenever, wherever and however you want
Online reader with customised display settings for better reading experience
What do you get with video?
Download this video in MP4 format
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with video?
Stream this video
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with Audiobook?
Download a zip folder consisting of audio files (in MP3 Format) along with supplementary PDF
What do you get with Exam Trainer?
Flashcards, Mock exams, Exam Tips, Practice Questions
Access these resources with our interactive certification platform
Mobile compatible-Practice whenever, wherever, however you want
BUY NOW $10 p/m for first 3 months. $15.99 p/m after that. Cancel Anytime!
Subscription
What do you get with a Packt Subscription?
This book & 7000+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook + Subscription?
Download this book in EPUB and PDF formats, plus a monthly download credit
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook?
Download this book in EPUB and PDF formats
Access this title in our online reader
DRM FREE - Read whenever, wherever and however you want
Online reader with customised display settings for better reading experience
What do you get with video?
Download this video in MP4 format
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with video?
Stream this video
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with Audiobook?
Download a zip folder consisting of audio files (in MP3 Format) along with supplementary PDF
What do you get with Exam Trainer?
Flashcards, Mock exams, Exam Tips, Practice Questions
Access these resources with our interactive certification platform
Mobile compatible-Practice whenever, wherever, however you want
  1. Free Chapter
    Getting Started with Grunt
About this book

A web application can quickly turn into a complex orchestration of many smaller components, each one requiring its own bit of maintenance. Grunt allows you to automate all the repetitive tasks required to get everything working together by using JavaScript, the most popular programming language.

Grunt Cookbook offers a host of easy-to-follow recipes for automating repetitive tasks in your web application's development, management, and deployment processes. This book will introduce you to methods that can be used to automate basic processes and your favorite tools. By following the recipes, you will soon be comfortable using Grunt to perform a wide array of advanced tasks in a range of different scenarios.

Publication date:
June 2015
Publisher
Packt
Pages
286
ISBN
9781783286515

 

Chapter 1. Getting Started with Grunt

In this chapter, we will cover the following recipes:

  • Installing the Grunt CLI

  • Installing Grunt on a project

  • Installing a plugin

  • Setting up a basic web server

  • Watching files for changes

  • Setting up LiveReload

  • Processing only changed files

  • Importing external data

 

Introduction


Grunt is a popular new task automation framework built upon the Node.js platform. It offers a wide range of features that allow you to streamline your project workflow and save time and energy by automating repetitive tasks, such as checking code quality, running tests, compiling templates and code, publishing to various types of services, and much more.

Task automation has been around since the beginning of software development and can probably be seen as a prominent reason for it being around at all. We're mostly writing programs to automate repetitive tasks after all.

Grunt itself is, for the most part, only a highly pluggable framework that provides a consistent interface for configuring automated tasks. The actual logic of the tasks is provided by a large variety of modules called plugins, which make use of this framework and usually tend to specialize in a certain set of functionalities.

At the time of writing the Grunt project is more than 3 years old, has over 3,000 plugins available in the npm public package registry, and provides tools and guides for creating or contributing to existing plugin projects.

A vast number of projects are currently making active use of Grunt in various ways, some of the most notable being the Yeoman, Modernizr, AngularJS, and JQuery projects.

Tip

Be sure to pay a visit to the Grunt website, if you've not already done so. It's filled with excellent guides and documentation, and it's the best place to find the plugins you need. The website can be found at the following URL:

http://gruntjs.com/

 

Installing the Grunt CLI


In order to make use of a Grunt configuration file, the Grunt command-line interface (CLI) tool needs to be installed.

Command-line tools such as the Grunt CLI are usually installed globally. This means that they are installed on top of the Node.js installation that is currently active in your terminal, and not in the current project path, as is usually the case.

Tip

In this book, we'll work with version 0.4.x of Grunt, which requires Node.js version 0.8.x or higher.

How to do it...

The following steps will take us through installing the Grunt CLI and testing for its successful installation.

  1. Assuming that you already have a global installation of Node.js, the following is the command to install the Grunt CLI:

    $ npm install --global grunt-cli
    
  2. If the installation was successful, the grunt command should now be available on the terminal. Test this by typing grunt in your terminal and confirm that it returns a message similar to the following:

    grunt-cli: The grunt command line interface. (v0.1.13)
    
    Fatal error: Unable to find local grunt.
    
    If you're seeing this message, either a Gruntfile wasn't found or grunt hasn't been installed locally to your project. For more information about installing and configuring grunt, please see the Getting Started guide:
    
    http://gruntjs.com/getting-started
    

How it works...

The npm install command looks up the grunt-cli package on npm's public package registry, and proceeds to download and install it once it is found.

Using the -g argument along with the install command indicates that the package we'd like to install, should be installed globally, meaning it should be installed on the version of Node.js that is currently active in our terminal.

In a default Node.js setup, a folder for executable binaries will automatically be added as a path that should be scanned by the terminal for executable commands. This makes the grunt command automatically available after the installation of this package, as its executable binary is provided and indicated in the package's installation information.

 

Setting up Grunt in a project


For a project to make use of the Grunt framework, it will require the installation of its libraries and the setting up a bare minimum configuration file. The libraries provide the framework and tools required by all Grunt plugins, and the configuration file provides a starting point from which we can start loading plugins and adjusting their behavior.

Getting ready

It's usually a good idea for a project to be packaged in a way to help keep track of dependencies, binaries, scripts, maintainers, and other important information. The standard package format for Node.js-based projects is CommonJS.

Tip

To find out more about CommonJS, you can take a look at its specification at the following URL:

http://wiki.commonjs.org/wiki/Packages/1.1

At the heart of the CommonJS package, lies the package.json file. This file contains everything important about the package and is stored in the JSON format. The simplest way to create a package.json file is to use the npm init command. This command will ask a series of questions and generate a package.json file based on the answers provided. Here's an example of the questions that are asked when you run the command:

name: (grunt-book) myproject
version: (0.0.0)
description: My first Grunt project.
entry point: (index.js)
test command:
git repository:
keywords:
author:
license: (ISC)

After these questions are answered, a package.json file will be generated in the current path with the following contents:

{
  "name": "myproject",
  "version": "0.0.0",
  "description": "My first Grunt project.",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

Tip

Another handy guide to the package.json file can be found at the following URL:

http://browsenpm.org/package.json

How to do it...

The following steps take us through installing the Grunt framework libraries on our project and creating a bare minimum configuration file.

  1. First, we'll install the Grunt libraries in the current project path, and have it added to our project dependencies. This can all be done by using the following command:

    $ npm install --save grunt
    

    Tip

    Due to our use of the --save flag with the install command, the Grunt package will be added to the dependency list of the project's package. This can be confirmed by taking a look at the dependencies property inside the package.json file.

    The --save-dev flag is also available for use with the install command when you'd like the installed packages to be added to the devDependencies property, which lists the dependencies to set up a development environment.

  2. Next, we'll set up an empty configuration file that would, at the very least, allow Grunt to run and also provides a place for future task configurations. Let's create a file called Gruntfile.js in the root directory of our project with the following contents:

    module.exports = function (grunt) {
      grunt.initConfig({});
      grunt.registerTask('default', []);
    };
  3. Now that we have the Grunt libraries installed and a basic configuration file set up, we can use the grunt command to test that it's all working as expected. Running the grunt command in the terminal should now produce output similar to the following:

    Done, without errors.
    

    Tip

    Running the grunt command without specifying any parameters will always try to run the default task, which in the case of our current example, is set to nothing.

How it works...

When the Grunt CLI tool is used, it always looks for the nearest file named Gruntfile.js, from which it then attempts to load configurations. Inside the configuration file, there is an exported function that receives one argument. This argument is an object that provides us with access to the Grunt framework to load, create, and configure tasks.

At this point, we have no tasks loaded or created, and no configurations defined. Our default task is also set to do nothing, so running the grunt command did nothing except report that it was successfully completed.

 

Installing a plugin


All of the functionality that can be provided by Grunt is housed in the plugins that are made available in the form of Node.js packages. In this recipe, we'll run through the process of installing a plugin, which will prepare us for all the recipes that are to follow.

For our example, we'll install the contrib-jshint (0.10.0) plugin. The same steps used to install this plugin can be used to install any of the other plugins available in the plugin package index.

Getting ready

In this example, we'll work with the basic project structure we created in the Setting up Grunt on a project recipe of this chapter Be sure to refer to it if you are not yet familiar with it's contents.

How to do it...

The following steps take us through installing a plugin on our project and loading the tasks it contains:

  1. The first step of installing a plugin is to install the package that contains it in the current project path. For our example, we'll install the contrib-jshint plugin that is contained in the grunt-contrib-jshint package. We can install this package and add it to our project dependencies by using the following command:

    $ npm install --save grunt-contrib-jshint
    
  2. Next, we'll have to load the tasks contained in the plugin package so they can be used in our configuration. This is done using the loadNpmTasks function, provided to us by the grunt object that is passed to us in the configuration file. After adding this, our configuration file should look similar to the following:

    module.exports = function (grunt) {
      grunt.initConfig({});
      grunt.loadNpmTasks('grunt-contrib-jshint');
      grunt.registerTask('default', []);
    };
  3. Now that we have the package installed and its tasks loaded, we can use the loaded tasks in our configuration. For our example, we had the jshint task loaded, which enables us to use it in a manner similar to the following:

    module.exports = function (grunt) {
      grunt.initConfig({
        jshint: {
          sample: {
            files: 'src/*.js'
          }
        }
      });
      grunt.loadNpmTasks('grunt-contrib-jshint');
      grunt.registerTask('default', []);
    };

There's more...

As you start to make use of more and more Grunt plugins, you will soon start to wonder whether there is a way to optimize the process a little. Fortunately, someone else has already gone down this road before and created the load-grunt-tasks utility that automates the loading of tasks from all the packages mentioned in your project dependencies. This means that we no longer need to add a loadNpmTasks call for each plugin we install.

The following steps illustrate the usage of this utility, continuing from the work we did earlier in the main recipe:

  1. Install the utility's package in the current project path and add it to your dependencies by using the following command:

    $ npm install --save load-grunt-tasks
    
  2. Now we can use it in our configuration file by importing the package and passing the grunt object to it. Now that we're making use of this utility, we can also remove all the loadNpmTasks we were using to load our plugins. This should result in a configuration file similar to the following:

    module.exports = function (grunt) {
      require('load-grunt-tasks')(grunt);
      grunt.initConfig({
        jshint: {
          sample: {
            files: 'src/*.js'
          }
        }
      });
      grunt.registerTask('default', []);
    };

    Tip

    The load-grunt-tasks plugin will, by default, only load plugins that have names with grunt at their start. This behavior can be customized by using the pattern option. To find out more about the load-grunt-tasks plugin, refer to the plugin's page at the following URL:

    https://github.com/sindresorhus/load-grunt-tasks

 

Setting up a basic web server


A simple web server will always come in handy during the development process of web-based projects. They can be easily set up and used to serve web content from your local machine, so you don't have to worry about constantly deploying your experimental changes to a remote service provider.

We'll make use of the contrib-connect (0.8.0) plugin, which provides us with the ability to set up and run a simple web server based on the Connect server framework. By default, it will only serve files from a directory, but it has the added benefit of being able to make use of the many Connect middleware plugins available.

Tip

You can read more about the Connect server framework and its middleware plugins at the following URL:

https://github.com/senchalabs/connect

Getting ready

In this example, we'll work with the basic project structure we created in the Setting up Grunt on a project recipe of this chapter. Be sure to refer to it if you are not yet familiar with it's contents.

How to do it...

The following steps take us through setting up a development server that serves files from a directory located in our project directory.

  1. We'll start by installing the package containing the contrib-connect plugin and loading its tasks as given in the instructions provided in the Installing a plugin recipe of this chapter.

  2. With the connect task loaded from the plugin, we can make use of it in our configuration. To do this, we add the following to our configuration:

    connect: {
      server: {
        options: {
          base: 'www',
          keepalive: true
        }
      }
    }

    Tip

    The base option indicates in which directory the server should look for the files that are requested. Everything that you'd like to serve with your development server can be placed in this directory. This includes HTML pages, JavaScript source files, style sheets, images, and so on.

    The keepalive option keeps the server running even if the requested task has finished. This is usually preferred if you're running the connect task on its own, but is not required if another task will be running indefinitely after it has completed.

  3. Let's add a simple file that we'd like to have served from our server so that we have something to test it with. Create a directory called www in the project root, and then a file inside it called index.html, with the following contents:

    <html>
      <head>
        <title>Test Page</title>
      </head>
      <body>
        <h1>This is a test page.</h1>
      </body>
    </html>

    Tip

    Like many other web servers, the Connect server started by this task will always look for an index.html file in a folder if no filename is specified in the request URL.

  4. Now we can run our web server using the grunt connect command, which will produce output indicating that our server started, along with a URL of where it can be reached:

    Running "connect:server" (connect) task
    Waiting forever...
    Started connect web server on http://0.0.0.0:8000
    
  5. Finally, we can use our favorite browser to pay a visit to the URL mentioned in the output. This would show us our example page, as served through our running server:

There's more...

The connect task provides many useful configuration options that allow us to automatically open a browser, specify the port and hostname where the server should run, serve files from more than one directory, use other Connect middleware plugins, and adding extra functionality to the created server.

Opening the default web browser on the default URL

In order to automatically open our favorite web browser on the default URL that our web server provides, we can set the open option to true as we do in the following example:

options: {
  base: 'www',
  keepalive: true,
  open: true
}

Opening a specific web browser at a specific URL

When we'd like to use a different web browser from the default one to open a URL other than the default one, we can provide an object to the open option that specifies exactly what we'd like. The following code specifies a URL, the browser that should be used to open it, and a callback function that should be called once it's been opened:

options: {
  base: 'www',
  keepalive: true,
  open: {
    target: 'http://localhost:8000/test.html',
    appName: 'firefox',
    callback: function() {
      console.log('Test URL opened in Firefox!');
    }
  }
}

Tip

Downloading the example code

You can download the example code files from your account at http://www.packtpub.com for all the Packt Publishing books you have purchased. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.

Using a specific port

The default port used by the connect task is 8000, but if you'd like to use a different one, the port option can be used to specify which one you'd like it to be, which is shown in the following code snippet:

options: {
  port: 3000,
  base: 'www',
  keepalive: true
}

Automatically selecting an available port

In case you are not sure about which ports will be available when you start the server, it can be quite useful for you to have the server automatically select an open port. Setting the useAvailablePort option to true will enable this behavior. The code snippet for this is as follows:

options: {
  base: 'www',
  keepalive: true,
  useAvailablePort: true
}

Using a specific hostname

In the case that you'd like the server to attach to a specific hostname and not just the default 0.0.0.0, you can make use of the hostname option in the following manner:

options: {
  base: 'www',
  keepalive: true,
  hostname: 'something.else.com'
}

Serving files from multiple directories

If you have more than one directory that contains the files that you'd like to be serving, then the base option can be provided with an array of directory names to look up content. Here is a code snippet for your reference:

options: {
  base: ['www', 'other'],
  keepalive: true
}

Tip

When using an array for the base option, the server will look up the requested resources in each of the directories, from left to right, and return a resource as soon as it's found. If each of the two directories in the example contained an index.html file, a request to the root URL would return the index.html file in the www directory.

Using middleware

If we'd like to use one of the many existing middleware plugins available for the Connect framework, we can set the middleware option to a function that modifies the middleware stack by adding the desired middleware to it.

  1. First, we'll need to install the middleware that we'd like to use, which in our case is packaged along with the Connect server framework. We can install the framework package using the following command:

    $ npm install --save connect
    
  2. Now, we alter the options of the server target in the connect task so that it adds the compress middleware to the stack:

    options: {
      base: 'www',
      keepalive: true,
      middleware: function(connect, options, middlewares) {
        middlewares.push(
          require('connect').middleware.compress()
        );
        return middlewares;
      }
    }

    Tip

    The middleware option can also be set to an array, but this will replace the default stack that is provided by the connect task. The default middleware allows the serving of files from the directories indicated by the base option.

Adding functionality to the created server

There are times that it would be useful to work with the server that is created by the connect task. A good example of this is when we'd like to enable our server to handle Socket.IO interactions. This can be done by providing a function to the onCreateServer option that works with the created server in whichever way you like:

options: {
  base: 'www',
  keepalive: true,
  onCreateServer: function(server, connect, options) {
    var io = require('socket.io').listen(server);
    io.sockets.on('connect', function (socket) {
      // do something with socket
    });
  }
}

Tip

This example assumes that you've already installed the socket.io package. You can find out more about socket.io at its website:

http://socket.io/

 

Watching files for changes


Another common requirement for development environments is the need to automatically run specific tasks when certain files are changed. This is especially useful when you'd like to monitor the quality of changing code in real time, or recompile altered resources as soon as they change, so that the effect of the changes are reflected without any manual intervention.

The contirb-watch (0.6.1) plugin allows us to keep a watch on a specific set of files, and run a specified set of tasks when file events are observed.

Getting ready

In this example, we'll work with the basic project structure that we created in the Setting up Grunt on a project recipe of this chapter. Be sure to refer to it if you are not yet familiar with it's contents.

How to do it...

The following steps take us through setting up a watch task that initiates a code quality analysis on a JavaScript source file each time a change to the file is observed.

  1. We'll start by installing the package that contains the contrib-watch plugin and loading its tasks by following the instructions provided in the Installing a Plugin recipe of this chapter.

  2. For our example, we'll make use of the jshint task to analyze the quality of a JavaScript source file. Let's install the contrib-jshint plugin and load its tasks by following the instructions provided in the Installing a Plugin recipe of this chapter.

  3. We'll also need a JavaScript source file that we can watch for changes and perform a quality analysis on. Let's create a file called sample.js in our project root and provide it with the following contents:

    var sample = 'Sample';
    console.log(sample);
  4. Now, we can set up the example jshint task, which we'll run using the watch task by adding the following to our configuration:

    jshint: {
      sample: {
        src: ['sample.js']
      }
    }
  5. With the plugin installed and the sample task configured, we can now configure a target on the watch task, which will run the jshint task every time the sample file called sample.js changes. This is done by adding the following to our configuration:

    watch: {
      sample: {
        files: ['sample.js'],
        tasks: ['jshint']
      }
    }
  6. Finally, we can start the task using the grunt watch command, which should produce the following output to confirm that it's running:

    Running "watch" task
    Waiting...
    
  7. To test our setup, we can now make some changes to the sample.js file and save them. This should produce output informing us of the file event similar to the following:

    Running "watch" task
    Waiting...
    >> File "sample.js" changed.
    Running "jshint:sample" (jshint) task
    >> 1 file lint free.
    
    Done, without errors.
    Completed in 1.0s at Wed Jan 1 2014 00:00:00 GMT - Waiting...
    

There's more...

The watch task plugin provides many more useful configuration options that allow us to watch more than one file, run a series of tasks, prevent process spawning for task runs, enable the interruption of task runs, specify the waiting period before rerunning tasks, run tasks only on specific file events, allow tasks to kill the watcher process, and run tasks once when the watcher starts up.

Watching more than one file

In case we'd like to watch more than one file, the pattern-matching capabilities of the standard Grunt files configuration can be used. The following configuration example will observe all the files in the project root or any of its sub directories with the txt extension:

watch: {
  sample: {
    files: ['**/*.txt'],
    tasks: ['sample']
  }
}

Tip

You can find out more about the file's configuration and the globbing patterns it supports at the following URL:

http://gruntjs.com/configuring-tasks#files

Running a series of tasks

In case we'd like to run more than one task each time a file event is observed, we can just add the tasks to the array passed to the tasks configuration:

watch: {
  sample: {
    files: ['sample.txt'],
    tasks: ['sample', 'another', 'finally']
  }
}

Tip

The tasks specified in using the tasks configuration are run one at a time, in the order they are placed inside the array.

Preventing process spawning for task runs

The default behavior of the watch task is to start each of the triggered tasks in their own child process. This prevents failing triggered tasks from causing the watch task itself to fail. As a side effect, it also clones the context of the watcher process for each task. This behavior can however be disabled by setting the spawn option to false, which triggers tasks a little faster and allows them to share a context between them. The following demonstrates the configuration for this:

watch: {
  sample: {
    files: ['sample.txt'],
    tasks: ['sample'],
    options: {
      spawn: false
    }
  }
}

Enabling the interruption of task runs

The default behavior for the watcher is to wait for the completion of the tasks triggered by the previous change, before waiting for changes again. By setting the interrupt option to true, the watcher will stop running tasks when a change is detected and start rerunning them. The following demonstrates the configuration for this:

watch: {
  sample: {
    files: ['sample.txt'],
    tasks: ['sample'],
    options: {
      interrupt: true
    }
  }
}

Specifying the waiting period before rerunning tasks

The default period the watcher will wait before checking for file changes after a previous task run is 500ms. This amount of time can be changed by setting the debounceDelay option to the number of milliseconds you'd like for it to wait. The following demonstrates the configuration for this:

watch: {
  sample: {
    files: ['sample.txt'],
    tasks: ['sample'],
    options: {
      debounceDelay: 1000
    }
  }
}

Run tasks only on specific file events

In addition to being changed, files can also be added and deleted. The default behavior of the watcher is to observe all these events, but if you'd like it to run tasks only on specific events, the event option can be set to either changed, added, deleted, or all.

The following example will only start the sample task if a file named sample.txt is added or deleted to the same path as the configuration file:

watch: {
  sample: {
    files: ['sample.txt'],
    tasks: ['sample'],
    options: {
      event: ['added', 'deleted']
    }
  }
}

Allowing tasks to kill the watcher process

Warnings and failures that are raised by the tasks started by the watch task will, by default, not interrupt its execution. Setting the forever option to false will disable this behavior and allow child tasks to stop the watcher process on warnings and failures. The following demonstrates the configuration for this:

watch: {
  options: {
    forever: false
  },
  sample: {
    files: ['sample.txt'],
    tasks: ['sample']
  }
}

Tip

Note that the forever option is a task-level option only and cannot be specified for individual targets.

Running tasks once at startup of the watcher

In the case that you'd like to run the tasks specified in the tasks option once the watcher starts up, and not only once file events are observed, you can set the atBegin option to true. The following demonstrates the configuration for this:

watch: {
  sample: {
    files: ['sample.txt'],
    tasks: ['sample'],
    options: {
      atBegin: true
    }
  }
}
 

Setting up LiveReload


Once you've got a development server running that serves the pages, code, and assets that make up your web application, you will notice that you constantly have to refresh the browser each time you wish to observe a change that was made.

This is where the LiveReload tool and its constituent libraries come in handy. It's designed to automatically reload the browser's contents if a page or code file is changed, and even apply changes to CSS and images live, without refreshing the browser's contents.

We can set up LiveReload for our project with the help of two plugins that are discussed in other parts of this chapter. The development server provided by the contrib-connect (0.8.0) plugin can be configured to accept LiveReload triggers, and the contrib-watch (0.6.1) plugin to send them file events.

Getting ready

In this example, we'll work with the basic project structure that we created in the Setting up Grunt on a project recipe of this chapter. Be sure to refer to it if you are not yet familiar with it's contents.

We also make use of the contrib-connect and contrib-watch plugins in this recipe, which have each been discussed separately in the Setting up a basic web server and Watching files for changes recipes of this chapter. These are to be referred to if you are not yet familiar with the plugins they discuss.

How to do it...

The following steps take us through setting up a watch task that will automatically trigger a refresh when it observes changes to an HTML page that is served from a local development server.

  1. We'll start by installing the packages containing the contrib-connect and contrib-watch plugins and loading their tasks by following the instructions provided in the Installing a plugin recipe of this chapter.

  2. For the sake of our example, we'll create a file called index.html in the www directory, which will be the file that we wish to view in a browser and have automatically updated when changes are made to it. The contents for this file follows:

    <html>
      <head>
        <title>LiveReload Test</title>
      </head>
      <body>
        <h1>First there was this.</h1>
      </body>
    </html>
  3. Next, we'll set up our development server, which will serve the index.html file from the www directory. Along with the standard configuration, we'll set the livereload option to true, indicating that the development server should enable the browser to receive LiveReload triggers. This is all done by adding the following to our configuration:

    connect: {
      dev: {
        options: {
          base: 'www',
          livereload: true
        }
      }
    }

    Tip

    Providing the true value for the livereload option includes connect-livereload in the middleware stack of the connect server. The middleware in turn inserts a snippet of code in the HTML code of the pages served, which enables the browser to accept LiveReload triggers.

    The keepalive option can be excluded from this configuration due to the watcher process that will continue to run after the server is started. This means that the Grunt process will not end, which would also have stopped the server it started.

  4. Now, we'll set up a watcher to observe file events in the www/index.html file. Along with the standard configuration, we'll set the livereload option to true, indicating that the appropriate LiveReload triggers should be sent whenever changes are observed. This is done by adding the following to our configuration:

    watch: {
      www: {
        files: ['www/index.html'],
        options: {
          livereload: true
        }
      }
    }
  5. Finally, we can start our server and watcher using the grunt connect watch command, which should produce output indicating the start of both:

    Running "connect:dev" (connect) task
    Started connect web server on http://0.0.0.0:8000
    
    Running "watch" task
    Waiting...
    
  6. Now, we can use our favorite browser to open the URL mentioned in the output, which should show us our sample page as served by the server:

  7. Let's try out the LiveReload functionality by changing the www/index.html file and saving it. This action should produce the following output in the terminal in which the server and watcher were started:

    >> File "www/index.html" changed.
    Completed in 0.001s at Wed Jan 01 2014 00:00:00 GMT – Waiting...
    
  8. Switching back to our browser that currently has the http://0.0.0.0:8000 URL open, we should now see the updated page, without having initiated a manual refresh:

 

Processing only changed files


When running a task that processes files in one way or another, you'll soon realize that you probably don't want it to process all the files each time it is performed. This is especially true when the amount of files the task has to process becomes quite large.

This is where the newer (0.7.0) plugin can help out by ensuring that only the files that have changed since the task's previous run are processed, each time it is called to run. It can be used with any plugin that makes use of the standard files configuration and becomes especially useful when using a watcher to rerun tasks each time a file change is detected.

Getting ready

In this example, we'll work with the basic project structure that we created in the Setting up Grunt on a project recipe of this chapter. Be sure to refer to it if you are not yet familiar with it's contents.

How to do it...

The following steps take us through making use of the newer plugin to check the code quality of only the JavaScript source files that have changed since the last run.

  1. We'll start by installing the package containing the newer plugin and loading its tasks by following the instructions provided in the Installing a plugin recipe of this chapter.

  2. For the purpose of our example, we'll also install the contrib-jshint plugin and load its tasks by following the instructions provided in the Installing a plugin recipe of this chapter.

  3. With all the required plugins installed, we can now add sample configuration for the jshint task, which will perform a code quality check on all the JavaScript files in the src directory. This is done by adding the following to our configuration:

    jshint: {
      sample: {
        src: 'src/**/*.js'
      }
    }

    Tip

    Note that the jshint plugin requires you to specify the files that are to be targeted by the task in the src configuration directly inside the target. In other cases, it is usually recommended to specify your target files using the files configuration.

  4. Now we can run the jshint task a couple of times using the grunt jshint command to observe its behavior. Each time we run it, we'll see that it scans all the files in the src directory. This should produce the same output each time, similar to the following:

    Running "jshint:sample" (jshint) task
    >> 3 files lint free.
    
  5. In order to make use of the newer plugin, we prepend newer: to the name of the task we wish to run. On running the grunt newer:jshint command the first time, the newer plugin will cache the timestamps of the files that have been processed. This produces output similar to the following:

    Running "newer:jshint" (newer) task
    
    Running "newer:jshint:sample" (newer) task
    
    Running "jshint:sample" (jshint) task
    >> 3 files lint free.
    
    Running "newer-postrun:jshint:sample:.cache" (newer-postrun) task
    
  6. When we run the grunt newer:jshint command again, we'll see that no files are processed by the jshint task, which produces output similar to the following:

    Running "newer:jshint" (newer) task
    
    Running "newer:jshint:sample" (newer) task
    No newer files to process.
    
  7. Now, we can change one of the files in the src directory and run the command again to see that the changed file is processed again:

    Running "newer:jshint" (newer) task
    
    Running "newer:jshint:sample" (newer) task
    
    Running "jshint:sample" (jshint) task
    >> 1 file lint free.
    
    Running "newer-postrun:jshint:sample:1:.cache" (newer-postrun) task
    

There's more...

Processing only changed files becomes especially useful when making use of the watch task provided by the contrib-watch plugin. A watch task will rerun its indicated tasks every time it observes a file change, which can happen quite often during development, and can take quite a bit of time if the tasks target a large number of files.

The following steps provide an example of how to use the newer plugin in conjunction with contrib-watch and continues with the work we did in the main recipe:

  1. We'll start by installing the package that contains the contrib-watch plugin and loading its tasks by following the instructions provided in the Installing a plugin recipe of this chapter.

  2. Now, we'll add a watch task, which will run the jshint task when it observes changes on any of the JavaScript files contained in the src directory. We'll also prepend the jshint task with newer: to indicate that we only want to process the files that actually changed. This is done by adding the following to our configuration:

    watch: {
      jshint: {
        files: ['src/**/*.js'],
        tasks: ['newer:jshint']
      }
    }
  3. Now, we can start the watch task by using the grunt watch command in the terminal. This should produce output similar to the following, indicating that the watcher is running:

    Running "watch" task
    Waiting...
    
  4. If we now change and save a file in the src directory, we should see output similar to the following, indicating that only the file that was changed has been processed by the jshint task:

    >> File "src/file.js" changed.
    Running "newer:jshint" (newer) task
    
    Running "newer:jshint:sample" (newer) task
    
    Running "jshint:sample" (jshint) task
    >> 1 file lint free.
    
    Running "newer-postrun:jshint:sample:1:.cache" (newer-postrun) task
    
 

Importing external data


In most coding practices, it's best practice to keep logic and data separated as much as possible. The same rule applies to the Grunt configuration logic and the data that it makes use of.

A very common use case for using data from an external source is the use of the project information contained in the package.json file. Information such as project names and version numbers might not change too often, but when they do, we'd probably prefer not having to look for them everywhere in the project.

Fortunately, the Grunt framework provides us with functions that allow us to easily read data from external files and store them in the configuration. This stored data can then also be easily used with the help of the string templates that are automatically processed for all configurations.

Getting ready

In this example, we'll work with the basic project structure that we created in the Setting up Grunt on a project recipe of this chapter. Be sure to refer to it if you are not yet familiar with it's contents.

How to do it...

The following steps take us through making use of data from the package.json file when generating an optimized version of a JavaScript source file.

  1. For our example, we'll make use of the contib-uglify plugin, which can be used to compress JavaScript source files. Let's install it and load its tasks for using the instructions provided in the Installing a Plugin recipe of this chapter.

  2. We'll also need a simple JavaScript source file for the sake of our example. Let's create a file called sample.js in the root of our project directory and fill it with the following code:

    module.exports = function () {
      var sample = 'Sample';
      console.log(sample);
    };
  3. Next, we'll import the data contained in our project's pacakge.json file by making use of the grunt.file.readJSON function and assigning its result to a property called pkg in our configuration. After adding this, our configuration object should look similar to the following:

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

    Tip

    Note that the property name pkg is just used for this example, and can be pretty much anything except for the names of the tasks that are available for configuration.

  4. Now that we have the data imported form our project package, we can set up an uglify task, which will compress a JavaScript source file called sample.js, using the version number contained in the package.json file as part of the resulting file's name. This is done by adding the following to our configuration:

    uglify: {
      sample: {
        files: {
          'sample_<%= pkg.version %>.js': 'sample.js'
        }
      }
    }

    Tip

    Grunt makes use of the Lo-Dash string template system. You can read more about it at the following URL:

    http://lodash.com/docs/#template

  5. Finally, we can use the grunt uglify command to test our setup, which should produce output similar to the following:

    Running "uglify:sample" (uglify) task
    File sample_0.0.0.js created: 81 B → 57 B
    
  6. We can now also take a look at our newly generated compressed version of the sample.js file in the sample_0.0.0.js file, which should have content similar to the following:

    module.exports = function(){var a="Sample";console.log(a)};

There's more...

The YAML format provides another popular way to store human readable data and can also be easily imported into our configuration. The following example, based on the work we did in the main recipe, demonstrates this functionality:

  1. First, we'll create a simple YAML file for the purpose of our example. Let's create sample.yaml in our project root and give it the following content:

    version: 0.0.0
    
  2. Now all we need to do is change the call to grunt.file.readJSON to import our sample YAML file instead. We do this by changing the pkg configuration to the following:

    pkg: grunt.file.readYAML('sample.yaml')
    
  3. If we now run the Grunt uglify command, we should see the same result as before, with output similar to the following:

    Running "uglify:sample" (uglify) task
    File sample_0.0.0.js created: 81 B → 57 B
    
About the Author
  • Jurie-Jan Botha

    Jurie-Jan Botha has been immersed in the computer programming world from a very early age. He taught himself by working his way through textbooks and later (when it became available) through the Internet. Being involved in a variety of projects, such as games, content management, and telecoms systems, throughout his career has provided him with a fairly good insight into selecting the right tools for the job and making optimal use of them. He's been using Grunt extensively for the past 3 years and is looking forward to using it in the foreseeable future.

    Browse publications by this author
Grunt Cookbook
Unlock this book and the full library FREE for 7 days
Start now