Intro To Gulp Part 1 - JavaScript

GulpJS

I recently started working with GulpJS as part of my build process and I have to say I am extremely impressed. I thought I would do a quick write up of how I used Gulp while working on a recent jQuery plugin and fell in love with the process.

What is Gulp?

If you are a Front-end Developer who has been living under a hole for the last few months, you might not have heard of Gulp yet. It is a javascript task runner, similar to GruntJS but is, to me anyway, a lot more intuitive. Why? It seems more like writing real javascript. It uses Node streams to send your code through the asset pipeline and can pretty much do anything that Grunt can do with less configuration. If you are interested in a more in depth look at what Gulp is, I highly recommend reading the documentation.

Getting Started

When you start up with Gulp, you obviously need to have Node installed. I won't go into how to install node here (just follow the documentation on the node website). If you haven't already installed Gulp just run the following code from the command line:

npm install -g gulp

That's all you need to get things started. Of course, like Grunt, there are a ton of plugins available to help you do things. So I wrote out a list of the tasks I wanted Gulp to do for me:

  • Automatically kick off a series of tasks when a file is modified
  • Create a minified version with .min extension
  • Copy the files to a distribution folder
  • Lint the files
  • Concatenate the files (if necessary)
  • Run Tests
  • Files should automatically contain a revision for cache busting

After searching through the Gulp plugins I found plugins that matched each of my needs and I installed them. The end.... but I'm guessing you probably want a little more detail than that. So let's go through it one step at a time and build up our base Gulpfile (the main javascript file that defines and executes our tasks).

Watching files for changes and copying them to new locations are functions that are built-in to Gulp so no plugins are needed. Let's just get that out of the way and start with a super simple Gulpfile.

var gulp = require( 'gulp' );

gulp.task( 'javascript', function() { 
  gulp.src( './src/**/*.js' ).pipe( gulp.dest( './dist/' ) );
)};  

gulp.task( 'watch', function() {
  gulp.watch( './src/**/*.js', ['javascript'] );
});

// The default task (called when you run `gulp` from cli)
gulp.task( 'default', ['watch']);

The first thing we do is include gulp in our file. Next, we define a task called javascript. This will contain everything that we want gulp to do when this task is run. Currently, we are just copying our existing JavaScript files to our dist folder. Then, we define a task called watch. It uses gulps built in monitoring to watch the folder and files specified for any changes and then runs the array of tasks provided. In this case, we are watching all the javascript files in our js folder (and all it's subfolders). We are only provide one task: our javascript task.

We can run this a couple of ways. To manually run the javascript task, just type gulp javascript on the command line from our main folder. We can run gulp watch to have gulp monitor our files for changes, and automatically run our javascript task. That's better... but we set our watch task to be the default one. So all we really have to do is run gulp from the command line and it will automatically run the watch task. Cool!

Two of our requirements are now complete.

  • Automatically kick off a series of tasks when a file is modified
  • Copy the files to a distribution folder

So let's get minification handled. I found the gulp-uglify plugin and installed it. Since I knew I would be using these in projects going forward I will install the plugins globally (using -g). If you do not want to do that just remove that flag from the examples and you will be good to go.

npm install -g gulp-uglify

Now let's update our Gulpfile.js.

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

gulp.task( 'javascript', function() {
  return gulp.src( './src/**/*.js' )  // folder(s) this task will run against
    .pipe( uglify() )  // minify the code
    .pipe( gulp.dest( './dist/' ) );  // copy original to destination folder
 )};

gulp.task( 'watch', function() {
  gulp.watch( './src/**/*.js', ['javascript'] );
});

// The default task (called when you run `gulp` from cli)
gulp.task( 'default', ['watch']);

Now before we copy our files over, we run each file through the minification process first. But wait that's not quite what we want... Yes, we want our javascript file copied to our dist folder but we want it to be have .min.js as well. Guess what? There is a plugin for that. It is called gulp-rename. So let's install that plugin as well.

npm install -g gulp-rename

Cool! Now let's modify our Gulpfile just a little bit.

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

gulp.task('javascript', function() {
  return gulp.src( './src/**/*.js' )  
    .pipe( gulp.dest( './dist/' ) )  // copy original to destination folder
    .pipe( uglify() )  // minify the code
    .pipe( rename( { suffix: '.min' } ) //rename to .min
    .gulp.dest( './dist/' ) );  // copy renamed file to destination folder
 )};

gulp.task( 'watch', function() {
  gulp.watch( './src/**/*.js', ['javascript'] );
});

// The default task (called when you run `gulp` from cli)
gulp.task( 'default', ['watch']);

Easy right? Now we have a copy of both the minified and un-minified file in our dist folder. We have successfully completed another of our requirements. Let's mark it off of our list and move on to the next item.

  • Create a minified version with .min extension

Linting your code

I think linting should be next on our list. There are two plugins I want to install for linting. The main one is jshint. This is our linting library and is all that is required to install. But, I also want to install jshint-stylish. This makes the output from jshint look a little, well, more stylish. First, let's install them both. Notice a slightly different syntax for jshint-stylish. This isn't a gulp plugin per se but instead a node package that works with our jshint library.

npm install -g gulp-jshint
npm install -g --save-dev jshint-stylish

And add them to our Gulpfile

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

gulp.task('javascript', function() {
  return gulp.src( './src/**/*.js' )  
    .pipe( jshint() )  // lint out file
    .pipe( jshint.reporter( 'jshint-stylish' ) )  // output to stylish
    .pipe( gulp.dest( './dist/' ) )  // copy original to destination folder
    .pipe( uglify() )  // minify the code
    .pipe( rename( { suffix: '.min' } ) //rename to .min
    .pipe( gulp.dest( './dist/' ) );  // copy renamed file to destination folder
 )};

gulp.task( 'watch', function() {
  gulp.watch( './src/**/*.js', ['javascript'] );
});

// The default task (called when you run `gulp` from cli)
gulp.task( 'default', ['watch']);

Woohoo! Now we are linting our code. And, for the paranoid out there, you can always re-lint your code after the minification process as well. I'm not doing it here for the sake of simplicity but it is something to consider. So we can mark off another one of our items off the list.

  • Lint the files

Testing your JavaScript

Well before we ship this off, we should probably make sure our javascript is passing their tests correctly. In this case, I used qunit. I chose this because I was working on a jQuery plugin. Since the jQuery team uses it to test their plugins et al, I did the same. Feel free to use whatever testing library you prefer. I am sure there is a gulp plugin for you.

I am going to skip the installation steps going forward ( npm install blah blah blah). I'm pretty sure you have got the idea by now. Let's update our Gulpfile again :-)

var gulp = require( 'gulp' );
var uglify = require( 'gulp-uglify' );
var rename = require( 'rename' );
var jshint = require( 'jshint' );
var qunit = require( 'gulp-qunit ');

gulp.task('javascript', function() {
  return gulp.src( './src/**/*.js' )  
    .pipe( jshint() )  // lint out file
    .pipe( jshint.reporter( 'jshint-stylish' ) )  // output to stylish
    .pipe( qunit() )  // run tests
    .pipe( gulp.dest( './dist/' ) )  // copy original to destination folder
    .pipe( uglify() )  // minify the code
    .pipe( rename( { suffix: '.min' } ) // rename to .min
    .pipe( gulp.dest( './dist/' ) );  // copy renamed file to destination folder
 )};

gulp.task( 'watch', function() {
  gulp.watch( './src/**/*.js', ['javascript'] );
});

// The default task (called when you run `gulp` from cli)
gulp.task( 'default', ['watch']);

Easy peasy. Another one down.

  • Run Tests

The Optional Stuff

Now, if you wanted I think you could just stop right there and have a really nice build system in place. But there are a few more things that I think can be useful. In some applications, you have multiple javascript files. In those cases, you should probably combine those into a single file for use in a production environment. If that is the case, you can use gulp-concat. Also, you might want to add a revision onto your file so that you can break the users' cache and force them to receive the updated file. For that you can use gulp-revision. Since they were some of my requirements listed above , let's include them. Lastly, I pulled out a lot of my strings and set them up as variables to make the code a little more DRY and easier to modify later on.

var gulp = require( 'gulp' ),
    uglify = require( 'gulp-uglify' ),
    rename = require( 'rename' ),
    jshint = require( 'jshint' ),
    qunit = require( 'gulp-qunit' ),
    rev = require( 'gulp-rev' ),
    concat = require( 'gulp-concat' ),
    source = './src/**/*.js',
    destination = './dist/', 
    filename = 'all.js',
    renameOptions = { suffix : '.min' };


gulp.task('javascript', function() {
  return gulp.src( source )  
    .pipe( concat( filename ) )  // combine our files
    .pipe( jshint() )  // lint out file
    .pipe( jshint.reporter( 'jshint-stylish' ) )  // output to stylish
    .pipe( qunit() )  // run tests
    .pipe( gulp.dest( destination ) )  // copy original to destination folder
    .pipe( uglify() )  // minify the code
    .pipe( rename( renameOptions ) // rename to .min
    .pipe( rev() ) // add revision to minified file
    .pipe( gulp.dest( destination ) );  // copy renamed file to destination folder
 )};

gulp.task( 'watch', function() {
  gulp.watch( source , ['javascript'] );
});

// The default task (called when you run `gulp` from cli)
gulp.task( 'default', ['watch']);

Now, we combine all of our javascript into one file called all.js and after we create a minified version of the file we add a revision suffix to the file before we move it to our dist folder. We can now mark our last two items off the list.

  • Concatenate the files (if necessary)
  • Files should automatically contain a revision for cache busting

The Little Extras

We have met all our requirements but I did run across a few other plugins that I thought would be useful. I don't think I need to explain how to add them into your gulpfile at this point.

There are plenty more quality plugins out there. These are just a couple that I ended up using in my javascript task.

  • Gulp Strip Debug - removes all those debugging console.log statements out of your code.
  • Gulp Notify - Notifys you when your tasks have been completed. Nice for long running tasks (which will appear in a later post). Also integrates with Growl which is a nice touch.
  • Gulp RimRaf - Cleans up your dist folder before you rebuild your files.
  • Gulp Bump - Used to update your semantic versioning.
  • Gulp Git - Source control integration.

I really like using Gulp over Grunt. I find the code easier to read, understand and modify. In a future post I will be adding additional tasks. The next one up: CSS! Happy Gulping!

Update: I finished up part 2!

'Til next time!

-G