Wednesday, July 29, 2015

JavaScript anno 2015 – Gulp

Gulp is an automation tool used for build automation. Just like MSBuild, Nant, make, and pretty much any build automation tool out there, Gulp runs one or more tasks that you define.

Gulp is built on Node.js which means that you define your tasks in JavaScript. To get started you need to install Gulp and create a gulpfile.js which will be the home for your automation tasks:
npm install gulp -g
npm install gulp --save-dev
new-item gulpfile.js -type file
invoke-item gulpfile.js


(btw I’m using Powershell as my shell)

If you run those 4 commands you should now have your gulp file open and ready to define some tasks. Here is an example of a Gulp task;
var gulp = require('gulp');

gulp.task('default', function() {
    process.stdout.write("Gulp is running the default task\n");
});

Since Gulp is running in Node.js we can use Node’s process object to write to the console. To run this task just run the gulp command in your shell and the default task will run.

Gulp doesn’t really do much itself. In fact, there’s only 4 functions in the Gulp API:
- src
- dest
- task
- watch

Src & Dest


src and dest are filesystem operations. To read files from the filesystem, use gulp.src(). To write files, use gulp.dest(). The src function takes a glob pattern as input (using Node’s glob which again uses the minimatch library), so to read all js-files in a scripts folder you can use this syntax to recursively find all files:
gulp.src('Scripts/**/*.js')

The dest function takes a path as parameter and will output files to that folder (and create the folder if it doesn’t exists). Matching files that already exists will be overwritten. To create a simple copy task, we can chain the src and dest functions together, and Gulp uses the pipeline pattern to do this (similar to pipes in Powershell and bash):
gulp.src('Scripts/**/*.js').pipe(gulp.dest('Copies'));


This will copy all js-files from the Scripts-folder to a folder called Copies. Note that it will keep the directory structure from the source, so if the file index.js exists in a folder ‘src’ in the Scripts folder it will end up in ‘Copies\src\index.js’.

Task


If we want to make a task for the file copying above we could define it like this:
gulp.task('copy', function(){
    gulp.src('Scripts/**/*.js')
        .pipe(gulp.dest('Copies'));
});


To run a specific task, we just use the gulp command and send in the name of the task as parameter:
gulp copy


If you don’t provide a named task, Gulp will look for a task called ‘default’ and run it.

The task function has two required parameters and one optional;
- name: the name of the task
- function: the function that defines what the task do
- dependencies (optional): an array of strings that contain names of other tasks that should be run prior to this one

As with all build automation tools, tasks can be chained together. If we want the default task to depend on the copy task, we can define it like this:
gulp.task('default', ['copy'], function() {
    process.stdout.write("Gulp is running the default task\n");
});


Watch


The watch function is (as src() and dest()) filesystem related and it takes a glob pattern as input. With watch() you can get notified when a file is changed and act accordingly. For instance if we want the copy task to automatically copy files when changes occur, we can setup a watch for that:
gulp.task('watch', function() {
    gulp.watch('Scripts/**/*.js', ['copy']);
});
When this task is running all changed files will be copied. Any new files will also be copied, but since the copy function only copies (doh!) it will not remove deleted files in the source folder from the destination folder. If we want that we need to add a new task that deletes files and that reacts to a ‘deleted’ event:
gulp.task('watch', function() {
    gulp.watch('Scripts/**/*.js', function(event){
        if(event.type === 'deleted') {
            deleteFile(event.path);
        }
        else {
            gulp.start('copy');
        };
    })
});


Note: I haven’t shown the implementation of the function deleteFile as this is just an example on how to react to different types of events. The available event types are: changed, added and deleted.

Plugins


Since Gulp itself doesn’t do much it depends upon plugins to provide the usefulness. And there’s a lot of plugins to choose from. At the time of writing there’s 1690 plugins listed on the Gulp home page. Let’s start with one of the most popular of them; the JSHint plugin. Since Gulp is running on Node it’s off course using npm so we install gulp-jshint just like any other npm package:
npm install gulp-jshint --save-dev


Now back to the Gulp file and let JSHint do some error checking on our JavaScript code:
var jshint = require('gulp-jshint');

gulp.task('jshint', function(){
    gulp.src('./src/scripts/*.js')
        .pipe(jshint())
        .pipe(jshint.reporter('jshint-stylish'));
});


Alternatives


Gulp is not the only build automation / task runner for JavaScript. It’s predecessor is Grunt and it’s quite similar to Gulp, but the definition of tasks are configuration-based and therefore more verbose. You configure tasks instead of defining them as JavaScript functions. Gulp is also more flexible than Grunt. Personally I prefer the Gulp-way of doing it, but Grunt is still the most popular task runner AFAIK.

Cake is pretty much the same as Gulp, but uses Coffeescript instead of pure JavaScript (as a side note; you can actually use Coffeescript with gulp too, but that’s another story).

Broccoli seems to be the new kid on the block right now and it seems promising. For bigger projects the watch-and-rebuild can take a ‘long’ time (everything is relative), so Broccoli was designed to do incremental builds. That is; only build whatever has changed since the last build. Broccoli is still in it’s early stages (at version 0.16 at the time of writing) and the “Windows support is still spotty” according to their own documentation.

Last but not least; you don’t actually need a dedicated automation tool as npm can already do this for you. I’m not going to go into detail on this one, but you can check out this blog post by Keith Cirkel for more information.

Resources


Gulp plugins – Search for available plugins
Node Glob – Documentation for the glob syntax in Node
Automate your tasks easily with Gulp.js by Justin Rexroad
Smashing Magazine: Building with Gulp
For more information on Node.js and npm, take a look at my previous post in this Getting started with JavaScript series: JavaScript anno 2015 - Node and npm

No comments: