In the previous post we discussed different aspects involved in the creation of maintainable JavaScript code. As part of this aspect we introduced JSHint and the automatic execution of the static code analysis with gulp. This article will enlarge set of tools that could automate several task involved in the workflow of developing JavaScript code.

Automation matters

When a task must be periodically completed by a developer, it is always a good idea to automate this operation. When a task is automated several benefits are achieved:

  1. Less error prone: Humans tend to make mistakes especially when a task /job is actually “donkey work”
  2. Time saving: The execution of automated task require almost zero effort.
  3. A simple task is executed regularly: When a task involves multiple step and those steps take time, it is likely that developer will not perform these task in short intervals. A good example is the execution of the unit-tests.
  4. Standardization: The heavy lifting needs only to be implemented in the build-script and the other (new) team-members could simply use the task or re-use the task in a new project.

Candidates for automation

We have seen in the previous post that the linting (static code analysis) could be performed with a build tool. Additionally we can automate the following tasks with a build tool /task runner:

  1. Execution of unit-tests
  2. Minify the source-code (JavaScript, CSS)
  3. Compile TypeScript or CoffeeScript to JavaScript
  4. Compile stylus to CSS
  5. Concatenation multiple files into one script file.

Build Tools

A developer could select from a variety of different build-tools when it comes to JavaScript. The most prominent build tools /task runner are Grunt and Gulp. The concept of both tools are the same:

  • The tool allow the definition of tasks and dependencies between task (execution order)
  • A task accepts one or multiple files are input (source)
  • The logic required for the different tasks is provided by plugins. Both tools support a huge set of plugins (grunt-plugins, gulp-plugins).
  • Both tools provided a command-line interface that is used to execute that task

Just like the build-tool Grunt provides Gulp a CLI (command line interface) tool that depends on Node.js platform. A quite good introduction is given in the article “Getting started with gulp” and in this article.

Lint Task

In the following we will setup a gulp-based build-script that performs the following task:

  1. Lint the source-code
  2. Compress /minify the source-code
  3. Clean-up
  4. Monitors file modifications
  5. (Executed unit-test)
    1. The execution of the unit-test will be covered in the next blog post

At first we need the “gulpfile.js” file that hosts all task and loads all dependencies (e.g. plugins) that are required for our sample script. In our first version of the build-script we will only define the lint-task and for this task we need several packages that are installed with npm (package manager for node.js).

 

  1. Gulp must be installed locally:
    1. npm install gulp –save-dev
    2. Note: Gulp must be installed globally (“npm install -g gulp”)
  2. JSHint Plugin:
    1. npm install gulp-jshint –save-dev
  1. To improve the readability of the generated reports, we recommend the Reporter “jshin-stylish-ex
    1. npm install jshint-stylish –save-dev

Once all dependencies are installed we can add the following code to our “gulpfile.js” file:

"use strict";

 

var gulp = require('gulp');

var jshint = require('gulp-jshint');

var stylish = require('jshint-stylish-ex');

 

// apply static code analysis

gulp.task('lint', function(){

 

    var source = [

        './lib/*.js',

        '!./lib/*.min.js'

    ];

 

    return gulp.src( source )

        .pipe( jshint( '.jshintrc' ) )

        .pipe( jshint.reporter( stylish ) );

});

This script performs the linting only on the JavaScript files in the lib folder (”./lib/*.js”) and not on the minified JavaScript files. Furthermore uses the task the extended report for the output of the JSHint-warnings. By executing the command “gulp lint” the task is executed:

 

The colour-schema used of the report is defined by the “.stylishcolors” file:

{

    "meta": "gray",

    "reason": "cyan",

    "verbose": "gray",

    "error": "red",

    "noproblem": "green"

}

As mentioned in the previous post, are the rules /options stored in the “.jshintrc” file (JSHint option documentation).

Compress Task

Next we will implement a task that minifies our files and store the compressed code into new files that use the suffix “min”. To do so we need to install some additional packages:

  1. Uglify plugin for gulp ( installation: “npm install gulp-uglify –save-dev”)
  2. Rename plugin for gulp (installation: “npm install gulp-uglify –save-dev”)
"use strict";

 

var gulp = require('gulp');

var jshint = require('gulp-jshint');

var stylish = require('jshint-stylish-ex');

var uglify = require('gulp-uglify');

var rename = require('gulp-rename');

var cleanup = require('gulp-clean');


...


// apply minifier

gulp.task('compress', ['cleanup','lint'], function(){

 

    var notifierOptions = {

        message: 'Compress completed',

        onLast: true

    };

 

    return gulp.src( ['./lib/*.js'] )

        .pipe( uglify() )

        .pipe( rename( { suffix: ".min" } ) )

        .pipe( gulp.dest('./lib'))

        .pipe( notify( notifierOptions ) );

});

When we now execute the command “gulp compress” will the build tools create the minified source-code in the lib folder. The code shows also how the gulp-notify plugin is used to write custom messages to the console.

Dependency Management for tasks

A requirement that often occur, is the definition of dependencies between tasks, where in the most simple case, the dependencies are added as second parameter to the task definition:

gulp.task('compress', ['lint'], function(){


     return gulp.src( ['./lib/*.js'] )

        .pipe( uglify() )

        .pipe( rename( { suffix: ".min" } ) )

        .pipe( gulp.dest('./lib'));

});

The current implementation of the “lint” task will not stop the execution of the next task that depends on the lint task. This behaviour still applies when the lint task detects an error in the source files.

To ensure that the compress task is only executed when no errors detected by JSHint we need to modify the task:

// apply static code analysis 

// stops the other tasks on error

gulp.task('lint-block', function(){


    var source = [

        './lib/*.js',

        '!./lib/*.min.js'

    ];


    return gulp.src( source )

        .pipe( jshint( '.jshintrc' ) )

        .pipe( jshint.reporter( stylish ) )

        .pipe( streamMap( function ( file, callback ) {


            callback( !file.jshint.success, file );

        }));

});

The only thing that is now missing, is a clean-up routine that deletes the minified files before the compress task is start. The plugin “gulp-clean” provides the kind of functionality:

Clean-up Task

When minifying JavaScript file, it is meaningful remove the outcome of the compress operation before a new compress-task is executed. This could be archived by using the “gulp-clean” plugin (installation: npm install gulp-clean –save-dev):

// delete the compressed files

gulp.task('cleanup', function(){

 

    var notifierOptions = {

        message: 'Cleanup completed',

        onLast: true

    };

 

    return gulp.src('./lib/*.min.js')

        .pipe( cleanup( { read: false } ) )

        .pipe( notify( notifierOptions ));

});

The task will delete all file in the “lib” sub-folder that have a filename that ends with “.min.js”.

Watch Task

To manually execute these individual tasks after every file modification does not comply with a good development process. There is need to immediately identify and highlight mistakes and errors in the code-base as soon as changes happen. We need to introduce a mechanism that executes a defined set of tasks every time there is a modification in a relevant file. This is exactly what the watch function of gulp provides.

The following task initializes a process that monitor specified files.

// process that monitors the files

gulp.task('watch', function(){

 

    var sourcefiles = [

        '.jshintrc',

        ' gulpfile.js',

        './lib/**.js' ]; 

 

    // adds a watch that executes the tasks 

    // everytime a file is modified 

    gulp.watch( sourcefiles, ['lint', 'compress'] );

});

When execution “gulp watch” on the console, the process initializes and invokes the task on every file modification. This approach ensures defective code is highlighted immediately in accordance with set lint guidelines.

Summary

In this blog post we have seen how a task-runner could automate operation and processes to make the development easier and ensure that guidelines are verified as a “natural” part of the development process.

The next post will discuss the area of unit-test and how to execute unit-tests with gulp.