You should automate every aspect of the development workflow to reduce the cost of building, deploying, and maintaining your application.
In this appendix we are going to introduce Grunt - a task runner framework for the JavaScript projects - that can help you with automation of repetitive operations like running tests when the code changes.
Grunt can watch your code changes and automate the process of running tests when the code changes. Tests should help in assessing the quality of our code.
With the Grunt tool you can have a script to run all your tests. If you came from the Java world, you know about Apache Ant, a general-purpose command-line tool to drive processes described build files as targets in the build.xml file. Grunt also runs the tasks described in scripts. There is a wide range of tasks available today - starting with running automated unit tests and ending with JavaScript code minification. Grunt provides a separate layer of abstraction where you can define tasks in a special DSL (domain-specific language) in Gruntfile for execution.
Let’s start with the simplest Grunt project setup. The following two files must be present in the project directory:
-
package.json
: This file is used by NPM to store metadata and a project dependencies.List Grunt and its plugins that your project needs as devDependencies in this file.
-
Gruntfile
: This file is namedGruntfile.js
orGruntfile.coffee
and is used to configure or define the tasks and load Grunt plugins.
module.exports = function (grunt) {
'use strict';
grunt.registerTask('hello', 'say hello', function(){ // (1)
grunt.log.writeln("Hello from grunt"); // (2)
});
grunt.registerTask('default', 'hello'); // (3)
};
-
Register a new task named
hello
. -
Print the greeting text using grunt’s log API.
-
With
grunt.registerTask
we define a default task to run when Grunt is called without any parameters.
Each task can be called separately from the command line by passing the task’s name as a command line parameter. For example, grunt hello
would only execute the task named "hello" from the above script.
Let’s run this hello
task with the following command
grunt --gruntfile Grunt_simple.js hello
.
Running "hello" task
Hello from grunt
Done, without errors.
Now after covering the basics of Grunt tool we can use it for something more interesting than just printing "hello world" on the screen. Since JavaScript is a interpreted language there is no compiler to help catch syntax errors. But you can use JSHint, an open source tool, which helps with identifying errors in JavaScript code in lieu of compiler. Consider the following JavaScript code.
var bonds = [ // (1)
'Sean Connery',
'George Lazenby',
'Roger Moore',
'Timothy Dalton',
'Pierce Brosnan',
'Daniel Craig', // (2)
//'Unknow yet actor'
] // (3)
-
We want to define an array that contains names of actors who played James Bond in a canonical series.
-
Here is example of a typical typo. A developer commented out the line containing an array element but kept the coma in previous line.
-
A missing semicolon is a typical typo. It is not an actual error, but omitting semicolon is not a good habit. An automatic semicolon insertion (ASI) will get you covered in this case.
In JavaScript, you can omit a semicolon between two statements written in separate lines. Automatic semicolon insertion is a source code parsing procedure that infers omitted semicolons in certain contexts into your program. You can read more about optional semicolons in JavaScript in the chapter "Optional Semicolons" in 'JavaScript. Definitive Guide. 6th Edition' book.
The above code snippet is a fairly simple example that can cause trouble and frustration if you don’t have proper tools to check the code semantics and syntax. Let’s see how JSHint can help in this situation. JSHint can be installed via NPM with command npm install jshint -g
. Now you can run JSHint against our code snippet:
> jshint jshint_example.js
jshint_example.js: line 7, col 27, Extra comma. (it breaks older versions of IE)
jshint_example.js: line 9, col 10, Missing semicolon. # (1)
2 errors # (2)
-
JSHint reports the location of error and a short description of the problem.
-
The total count of errors
Tip
|
WebStorm IDE has built-in support for JSHint tool. There is 3rd party plugin for Eclipse - jshint-eclipse. |
Grunt also has a task to run JSHint against your JavaScript code base. Here is how JSHint configuration in Grunt looks like.
module.exports = function(grunt) {
grunt.initConfig({
jshint: {
gruntfile: { // (1)
src: ['Gruntfile_jshint.js']
},
app: {
src: ['app/js/app.js']
}
}
});
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.registerTask('default', ['jshint']); // (2)
};
-
Because Gruntfile is JavaScript file, JSHint can check it as well and identify the errors.
-
When grunt will be run without any parameters, default task jshint will be triggered.
> grunt
Running "jshint:gruntfile" (jshint) task
>> 1 file lint free.
Running "jshint:app" (jshint) task
>> 1 file lint free.
Done, without errors.
Another handy task that to use in developer’s environment is the watch
task. The purpose of this task is to monitor files in pre-configured locations. When the watcher detects any changes in those files it will run the configured task. Here is how a watch task config looks like:
watch
task configmodule.exports = function(grunt) {
grunt.initConfig({
jshint: {
// ... configuration code is omitted
},
watch: { // (1)
reload: {
files: ['app/*.html', 'app/data/**/*.json', 'app/assets/css/*.css', 'app/js/**/*.js', 'test/test/tests.js', 'test/spec/*.js'], // (2)
tasks: ['jshint'] // (3)
}
}
});
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.registerTask('default', ['jshint']);
};
-
The
watch
task configuration starts here -
The list of the files that need to be monitored for changes
-
A array of tasks to be triggered after file change event occurs
> grunt watch
Running "watch" task
Waiting...OK
>> File "app/js/Player.js" changed.
Running "jshint:gruntfile" (jshint) task
>> 1 file lint free.
Running "jshint:app" (jshint) task
>> 1 file lint free.
Done, without errors.
Completed in 0.50s at Tue May 07 2013 00:41:42 GMT-0400 (EDT) - Waiting...