Book Image

Getting Started with Grunt: The JavaScript Task Runner

By : Jaime Pillora, Bocoup LLC
Book Image

Getting Started with Grunt: The JavaScript Task Runner

By: Jaime Pillora, Bocoup LLC

Overview of this book

Table of Contents (12 chapters)

Real-world use cases


Hearing about the benefits of Grunt is all well and good, but what about actual use cases that the average web developer will face every day in the real world? In this section, we'll take an eagle-eye view of the most common use cases for Grunt.

These examples make use of configuration targets. Essentially, targets allow us to define multiple configurations for a task. We'll cover more on configuration targets in Chapter 2, Setting Up Grunt.

Static analysis or Linting

In programming, the term linting is the process of finding probable bugs and/or style errors. Linting is more popular in dynamically typed languages as type errors may only be resolved at runtime. Douglas Crockford popularized JavaScript linting in 2011 with the release of his popular tool, JSLint.

JSLint is a JavaScript library, so it can be run in Node.js or in a browser. JSLint is a set of predetermined rules that enforce correct JavaScript coding practices. Some of these rules may be optionally turned on and off, however, many cannot be changed. A complete list of JSLint rules can be found at http://gswg.io#jslint-options.

This leads us to JSHint. Due to Douglas Crockford's coding style being too strict for some, Anton Kovalyov has forked the JSLint project to create a similar, yet more lenient version, which he aptly named: JSHint.

I am a fan of Douglas Crockford and his book, JavaScript—The Good Parts (http://gswg.io#the-good-parts), but like Anton, I prefer a more merciful linter, so in this example below, we will use the Grunt plugin for JSHint: http://gswg.io#grunt-contrib-jshint.

//Code example 04-linting
//Gruntfile.js
module.exports = function(grunt) {

  // Load the plugin that provides the "jshint" task.
  grunt.loadNpmTasks('grunt-contrib-jshint');

  // Project configuration.
  grunt.initConfig({
    jshint: {
      options: {
        curly: true,
        eqeqeq: true
      },
      target1: ['Gruntfile.js', 'src/**/*.js']
    }
  });

  // Define the default task
  grunt.registerTask('default', ['jshint']);

};

//src/foo.js
if(7 == "7") alert(42); 

In the preceding code, we first load the jshint task. We then configure JSHint to run on the Gruntfile.js file itself, as well as all of the .js files in the src directory and its subdirectories (which is src/foo.js in this case). We also set two JSHint options: curly, which ensures that curly braces are always used in if, for, and while statements; and eqeqeq, which ensures that strict equality === is always used.

JSHint has retained most of the optional rules from JSLint and it has also added many more. These rules can be found at: http://gswg.io#jshint-options.

Finally, we can run the jshint task with grunt, and we should see the following:

$ grunt
Running "jshint:target1" (jshint) task
Linting src/foo.js...ERROR
[L1:C6] W116: Expected '===' and instead saw '=='.
if(7 == "7") alert(42);
Linting src/foo.js...ERROR
[L1:C14] W116: Expected '{' and instead saw 'alert'.
if(7 == "7") alert(42);

Warning: Task "jshint:target1" failed. Use --force to continue.

Aborted due to warnings.

The result shows that JSHint found two warnings in the src/foo.js file on:

  • Line 1, column 6—since we've enforced the use of strict equality, == is not allowed, so it must be changed to ===.

  • Line 1, column 14—since we've enforced the use of the curly braces, the if statement body must explicitly use curly braces.

Once we've fixed these two issues as follows:

if(7 === "7") {
  alert(42);
}

We can then re-run grunt and we should see:

$ grunt
Running "jshint:target1" (jshint) task
>> 2 files lint free.

Done, without errors.

Notice that two files were reported to be lint free. The second file was the Gruntfile.js file, and if we review this file, we see it does not break either of the two rules we enabled.

In summary, JSHint is very useful as the first step of our Grunt build as it can help catch simple errors, such as unused variables or accidental assignments in if statements. Also, by enforcing particular coding standards on the project's code base, it helps maintain code readability, as all code entering the shared repository will be normalized to a predetermined coding style.

Transcompilation

Transcompiling—also known as source-to-source compilation and often abbreviated to transpiling—is the process of converting the source code of one language to the source code of another. Within the web development community in recent years, there has been an increase in the use of transcompile languages such as Haml, Jade, Sass, LESS, Stylus, CoffeeScript, Dart, TypeScript, and more.

The idea of transcompiling has been around since the 1980s. A popular example was an original C++ compiler (Cfront) by Bjarne Stroustrup, which converted C++ (known as C with Classes at the time) to C.

CoffeeScript

CoffeeScript (http://gswg.io#coffeescript) is the most popular transpile language for JavaScript. It was released in 2009 by Jeremy Ashkenas and is now the 10th most popular language on GitHub with 3 percent of the all code in public Git repositories. Due to this popularity, a particularly common use case for the modern web developer is to compile CoffeeScript to JavaScript. This can be easily achieved with the Grunt plugin http://gswg.io#grunt-contrib-coffee.

In the following example, we'll use the grunt-contrib-coffee plugin to compile all of our CoffeeScript files:

//Code example 05-coffeescript
module.exports = function(grunt) {

  // Load the plugin that provides the "coffee" task.
  grunt.loadNpmTasks('grunt-contrib-coffee');

  // Project configuration.
  grunt.initConfig({
    coffee: {
      target1: {
        expand: true,
        flatten: true,
        cwd: 'src/',
        src: ['*.coffee'],
        dest: 'build/',
        ext: '.js'
      },
      target2: {
        files: {
          'build/bazz.js': 'src/*.coffee'
        }
      }
    }
  });

  // Define the default task
  grunt.registerTask('default', ['coffee']);
};

Inside the configuration, the coffee object has two properties; each of which defines a target. For instance, we might wish to have one target to compile the application source and another target to compile the unit test source. We'll cover more on tasks, multitasks, and targets in Chapter 2, Setting Up Grunt.

In this case, the target1 target will compile each .coffee file in the src directory to a corresponding output file in the build directory. We can execute this target explicitly with grunt coffee:target1, which should produce the result:

$ grunt coffee:target1
Running "coffee:target1" (coffee) task
File build/bar.js created.
File build/foo.js created.

Done, without errors.

Next, target2 will compile and combine each of the .coffee files in the src directory to a single file in the build directory called bazz.js. We can execute this target with grunt coffee:target2, which should produce the result:

grunt coffee:target2
Running "coffee:target2" (coffee) task
File build/bazz.js created.

Done, without errors.

Combining multiple files into one has advantages and disadvantages, which we shall review in the next section Minification.

Jade

Jade (http://gswg.io#jade) compiles to HTML and, as with CoffeeScript to JavaScript, Jade has the semantics of HTML, though different syntax. TJ Holowaychuk, an extremely prolific open-source contributor, released Jade in July 2010. More information on the Grunt plugin for Jade can be found at http://gswg.io#grunt-contrib-jade.

We'll also notice the following example Gruntfile.js file is quite similar to the previous CoffeeScript example. As we will see with many Grunt plugins, both these examples define some kind of transform from one set of source files to another set of destination files:

//Code example 06-jade
module.exports = function(grunt) {

  // Load the plugin that provides the "jade" task.
  grunt.loadNpmTasks('grunt-contrib-jade');

  // Project configuration.
  grunt.initConfig({
    jade: {
      target1: {
        files: {
          "build/foo.html": "src/foo.jade",
          "build/bar.html": "src/bar.jade"
        } 
      }
    }
  });

  // Define the default task
  grunt.registerTask('default', ['jade']);
};

In this example, target1 will do a one-to-one compilation, where src/foo.jade and src/bar.jade will be compiled into build/foo.html and build/bar.html respectively. As we have set the default task to be the jade task, we can run all of jade's targets with a simple grunt command, which should produce:

$ grunt
Running "jade:target1" (jade) task 
File "build/foo.html" created.
File "build/bar.html" created.

Done, without errors.
Stylus

Stylus (http://gswg.io#stylus) compiles to CSS, and as before, it has the semantics of CSS though different syntax. TJ Holowaychuk also created Stylus, which he officially released in February 2011. More information on the Stylus Grunt plugin can be found at http://gswg.io#grunt-contrib-stylus. Similarly to the examples above, the following example Gruntfile.js file contains only slight differences. Instead of jade, we're configuring stylus, and instead of transpiling .jade to .html, we're transpiling .styl to .css:

//Code example 07-stylus
module.exports = function(grunt) {

  // Load the plugin that provides the "stylus" task.
  grunt.loadNpmTasks('grunt-contrib-stylus');

  // Project configuration.
  grunt.initConfig({
    stylus: {
      target1: {
        files: {
          "build/foo.css": "src/foo.styl"
        }
      }
    }
  });

  // Define the default task
  grunt.registerTask('default', ['stylus']);
};

When we run grunt, we should see the following:

$ grunt
Running "stylus:target1" (stylus) task
File build/foo.css created.

Done, without errors.
Haml, Sass, and LESS

Grunt plugins that transpile code are very similar, as previously seen with CoffeeScript, Jade and Stylus. In some way or another, they define a set of input files and a set of output files, and also provide options to vary the compilation. For the sake of brevity, I won't go through each one, but instead I'll provide links to each preprocessor (transcompiler tool) and its respective Grunt plugins:

  • Haml—http://gswg.io#haml—gswg.io#grunt-haml

  • Sass—http://gswg.io#sass—gswg.io#grunt-contrib-sass

  • LESS—http://gswg.io#less—gswg.io#grunt-contrib-less

At the end of the day, the purpose of using transcompile languages is to improve our development workflow, not to hinder it. If using these tools requires a lengthy setup for each, then the more tools we add to our belt, the longer it'll take our team to get up and running. With Grunt, we add each plugin to our package.json and with one npm install command, we have all the plugins we need and can start transpiling in minutes!