Gulp – Getting Started With Javascript Build Automation

Gulp helps us automate tasks within your javascript projects. Why would we want to do this? Well it helps us to be more productive. Not only do frontend developers have to sit down and write code, there is a ton of other work we have to do. Things like minification, concatenation, writing vendor prefixes, using LESS to compile CSS, optimizing third-party code like jQuery, Angular etc, injecting files into HTML, unit testing, caching templates, file revisions and versioning, code analysis, and the list goes on and on. As you can already tell, frontend development is no joke when done right. And here is where Gulp comes in.

You can delegate these tasks to gulp. By allowing you to provide files to it, it will alter them to a destination by streaming them through its pipeline. For example, want to minify a javascript file? Send it through the pipeline and out comes a file that is minimized! File goes in, and comes out new (like magic). The typical build pipeline can be described as follows.

Develop > Code Analysis > Testing > Build > Deploy

With Gulp, we can just type in one simple command and it will build and deploy our project!

Hello Gulp-Fundamentals !

lets create a sample hello world application called ‘gulp-fundamentals’. You can also follow alone by downloading the tutorial files from git. To start using Gulp however, please make sure that you have Node installed. There are many resources on the web on how to do that so i will not be covering that here. You also want to have Bower installed. If you dont know what  Bower is, please check out my tutorial on the fundamentals of bower here.

Install Gulp by running the npm command

npm install -g gulp

This will install Gulp on a global level. What this means is we can run gulp from the command line anyway. In our sample project however we are going to need to add it local as well. The way we do that is use the –save-dev option when we use NPM.

Using the command line, change directory into your project. We will use npm init to create our package.json file.

npm init

This will ask you a bunch of questions, and then write a package.json for you. Go ahead an accept the defaults for now or you can answer all the questions being asked. The most important thing now is that a package.json file is generated for us. After the file is generated lets install gulp locally into the project.

Install by doing this (Note: Please run these commands in your project folder)

npm install --save-dev gulp

We want this to happen so that anyone who installs our package will have gulp also installed (as they will need it to run the project). Another very important thing to note is that devDependencies are only run during development of our project. We do not push these into production. As you can already guess, there are 2 types of dependencies. Dependencies and devDependencies. Dependencies are needed to run the app, where devDependencies are not. One would not need JSHint or Karma (for testing) to run a final application, so packages like these are saved a devDependencies.

Next lets create a file in our project called ‘gulpfile.js’. This is where all of our gulp tasks we define in our project is going to run. Lets create our file task in this file

var gulp = require('gulp'); gulp.task('hello-gulp', function(){  console.log("Yay My First Gulp Task"); });

First we require to use the gulp module. Once we have that we can create a task by using gulp.task, passing a function to it(and options parameters if need be). In this case, we will console.log to the screen. Save these changes, change directory into this folder and run the command

gulp hello-gulp

There you go! We can see in our command-line returns a successful message. It tells us its using the gulpfile, then starts execution by outputting the console.log message, then ends the execution. Pretty exciting. Lets see what is going on here.

gulp.task( name [, dep], fn)

We have created a task called ‘name’. The name of that task is how we will run it and has to be unique. The second parameter is the function that is going to define that task. The function returns a gulp stream(in this case the operations we want to perform on the file(s)). We can optionally declare dependencies in the task. For example, we can have a dependency list that looks like this

gulp.task( 'MyTaskName', ['jscs','jshint'], function(){
  //other gulp code
} )

The dependencies are also tasks that you have already defined in your project. In that case you have a task called jscs and another called jshint. The dependencies will run in parallel before the gulp task called MyTaskName kicks off. We use gulp.task when you want to create a new task. Tasks such as minification, linting or unit testing, etc.

Using JSHint & JSCS in Gulp

I mentioned that you can add dependencies to your gulp build. Such as JSHint and JSCS to our project. JSHint is a static code analysis tool used in software development for checking if JavaScript source code complies with coding rules. JSCS is a code style linter for programmatically enforcing your style guide. You can configure JSCS for your project in detail using over 150 validation rules, including presets from popular style guides like jQuery, Airbnb, Google, and more. I believe it is clear why these tools should be run in your JS project before deploying to project.

Before we use any of these in our project, we have to install them. Here we need to install JSHint and JSCS

npm install --save-dev gulp-jshint
npm install --save-dev gulp-jscs

Now that they are installed we can go ahead an require these modules in our project.

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

With that in place lets create another task. We will call it inspector because it will do all the work of running our JS files through JSHint and JSCS. You are welcome to name the task differently if you want.

gulp.task('inspector', function () {
   return gulp
     .src([
      './*.js'
     ])
    .pipe(jshint())
    .pipe(jscs())
    .pipe(jshint.reporter('jshint-stylish', {verbose: true}));
});

Before we run this, lets create two new files which specify the options we want for JSHint and JSCS. These options communicate what rules should be applied to our code being processed. Create .jshintrc and .jscsrc in your project folder. Below is an example you can copy and paste in your files. Your work team will have to decide which rules to apply in the files.

.jshintrc file (copy below into file)


{
   "boss": true,
   "curly": true,
   "esnext": true,
   "eqeqeq": true,
   "eqnull": true,
   "expr": true,
   "immed": true,
   "noarg": true,
   "quotmark": "double",
   "smarttabs": true,
   "trailing": true,
   "unused": true
 }

.jsscrc file (copy below into file)


{
  "disallowMixedSpacesAndTabs": true,
  "disallowMultipleLineBreaks": true,
  "disallowMultipleSpaces": true,
  "disallowMultipleVarDecl": true,
  "disallowNamedUnassignedFunctions": true,
  "disallowNewlineBeforeBlockStatements": true,
  "disallowSpacesInCallExpression": true,
  "disallowSpacesInFunctionDeclaration": {
    "beforeOpeningRoundBrace": true
  },
  "disallowTrailingWhitespace": true,
  "esnext": true,
  "requireCommaBeforeLineBreak": true,
  "requireCurlyBraces": ["if", "else", "for", "while", "try", "catch"],
  "requireSemicolons": true,
  "requireSpaceBetweenArguments": true,
  "requireSpacesInConditionalExpression": true,
  "requireSpacesInForStatement": true,
  "requireSpacesInsideObjectBrackets": "all",
  "validateIndentation": "\t",
  "validateQuoteMarks": "\""
}

I will be explaining everything soon, but before i do please install the following to your project

npm install --save-dev jshint-stylish

If you look closely we used jshint-stylish in our pipe methods (above). We need to install it before running our project.

We are now ready to run our project!

gulp inspector

Running this will stream our code through the gulp pipe, where JSHint and JSCS will report back any errors in our code.

Lets take a look at what the inspector task is doing. We see something returned by the gulp stream ( gulp.src )

gulp.src (globs[, options])

The globs passed into this method is a file pattern match. It tells gulp which files to stream.  Lets see it in action (passing some options as well)

gulp.task( 'MyTaskName', ['jscs','jshint'], function(){
  return gulp
    .src('./client/js/**/*.js')
    //other gulp code
} )

The code above is a little different that we have seen. But the shell is the same. The only thing different is we are passing in the options parameter(more on that later). We specify the path: find the client folder then js folder within it, then any folder under that, and ultimately any files with a ‘.js’ extension. We can also specify other options within the src method. The first one is base

gulp.task( 'MyTaskName', ['jscs','jshint'], function(){
  return gulp
    .src('./client/js/**/*.js', {base:'./client/'})
    //other gulp code
} )

Here we are saying the base of the file patterning matching is client. This can sometimes be a little tricky to understand, so i will try my best. By specifying a base, the final build output does not include the base folder name ( ./build/js/somedir/somefile.js instead of ./client/js/somedir/somefile.js  ). Without specifying a base gulp resolves to a base of ‘client/js’. Please see this link for other options you can set. You can also exclude files by using ! in your path, like this

// Exclude node modules from linting
"!app/node_modules/**/*.js"

This excludes all files in that path. Another pattern you will see, is to create an array that holds all your paths and pass it into the src method

// Exclude node modules from linting
var myPaths = ["app/**/*.js","OTHER_PATHS"]
gulp.task('MyTaskName', function(){
  return gulp
    .src(myPaths)
});

After the source paths are specified we need to pipe the files in one of our plugins. Using an example from earlier, we can pipe it through jshint. We can also pipe the files to a destination. The destination specifies the folder name where all files should be sent to.

gulp.task( 'MyTaskName', ['jscs','jshint'], function(){
  return gulp
    .src('./client/js/**/*.js', {base:'./client/'})
    .pipe(jshint())
    .pipe(gulp.dest('./build'));
} );

We are sending the final file(s) through the pipeline to a final destination. We take the files in the path(src) and send it to the build folder after streaming it through pipeline.

Lets take this time to talk about more about options parameter. Within the task we can add an option of [‘jscs’, ‘jshint’]. Earlier on in the post i mentioned these dependencies/options will run in parallel before the gulp task runs. This can be helpful as you may want other things to run first. Let us create two tasks as dependencies so that the above examples make more sense.

gulp.task('jscs', function(){
  return gulp
    .src(myPaths)
    .pipe(jscs())
});
gulp.task('jshint', function(){
  return gulp
    .src(myPaths)
    .pipe(jshint())
    .pipe(jshint.reporter())
});

What we have done is create two tasks. Now we create a task that use these dependencies.

gulp.task( 'MyTaskName', ['jscs','jshint'], function(){
  //you can run another task if you want, or leave callback out
});

We create a new task(MyTaskName), and tell it to run jscs and jshint before running anything. Makes sense? You should also know that the JSHint reporter gets raw data and outputs into the console. jshint-stylish makes it look a little prettier when outputted. Find out more here. Lets move on!

Watch for file changes

When we need to watch our files for changes we use the watch method in gulp.

gulp.watch( glob [, options], tasks )

Let’s take a look at an example

gulp.task('lint-watcher', function () {  gulp.watch('./client/js/**/*.js', [  'jshint',  'jscs'  ]); });

Within watch we take a file pattern to match what files we are interested in, then we pass in names of tasks we want to perform when those file changes. In this case, when any of my files in the glob is matched run a task called jshint, which checks the code for synax errors. Instead of passing in tasks we can also pass in a callback function to be performed everytime the file changed.

gulp.task('lint-watcher', function () {  gulp.watch('./client/js/**/*.js', function(event){
  console.log('event type: ' + event.type + ', ' + 'event path: ' + event.path);
 }); });

Here what happens is, anytime there is a change in a javascript file, gulp.watch will execute a callback function. The function takes an event object which contains the type or path to that file.

Output files names to console

Sometimes you want a detailed report of the files that are being run through the pipeline. Say you have 7 javascript files and you want to log to the console which file is currently being processed during build, gulp gives us another plugin we can you called print.

gulp.task('inspector', function () {
   return gulp
     .src([
      './*.js'
     ])
    .pipe(myPrint())
    .pipe(jshint())
    .pipe(jscs())
    .pipe(jshint.reporter('jshint-stylish', {verbose: true}));
});

Before you using this make sure you install the module and import it into your project

var myPrint = require('gulp-print')
...
Then in the command line
npm install --save-dev gulp-print

Loading ALL gulp plugins

Dont worry, we are not going to load all gulp plugins by downloading them into our project, however what we are going to learn to do is have gulp automatically load plugins on the fly. In most projects, you may notice that your gulpfile grows significantly because there are just so many plugins being imported. Well there is yet another plugin that helps you load plugins on the fly. Let us comment out the following plugins in our gulpfile

//var jshint = require(‘gulp-jshint’);
//var jscs = require(‘gulp-jscs’);
//var myPrint = require(‘gulp-print’);

Instead we will import the gulp-load-plugins into our project

var s$ = require(‘gulp-load-plugins’);

Now that you have this in your file, be sure to do an npm install to make it available

npm install –save-dev gulp-load-plugins

Finally edit your code so your current plugins use gulp-load-plugins to download the files it needs. Lets see how

gulp.task('inspector', function () {
   return gulp
     .src([
      './*.js'
     ])
    .pipe(s$.print())
    .pipe(s$.jshint())
    .pipe(s$.jscs())
    .pipe(s$.jshint.reporter('jshint-stylish', {verbose: true}));
});

Notice that s$ is now prepended. The plugin does all the magic (via lazy loading what is needed). Notice that myPrint is now print. This is because we commented that code out, so we are using the raw print plugin instead of assigning it.

Creating a Gulp Config file

Sometimes as your code grows you might want to pull things out and put them into a configuration file. That can also be helpful if you simply wanted to edit values without touching the gulpfile. Earlier on i showed you how to pull the .src files into a array variable. Lets take it a little further by putting that into a config file instead. Create a new file in your project called gulp.config.js. This will be a new module. Add this to the file

module.exports = function () {  var appConfig = {  jsFiles: [  './client/js/**/*.js',  './*.js'  ]  };  return appConfig;  };

Now lets see how to use this in our gulpfile. Lets require our new config file

var config = require('./gulp.config')();

We require the new file we just created and execute it right away. Then we use it in our gulp task like this

gulp.task('inspector', function () {
   return gulp
    .src(config.jsFiles)
    .pipe(s$.print())
    .pipe(s$.jshint())
    .pipe(s$.jscs())
    .pipe(s$.jshint.reporter('jshint-stylish', {verbose: true}));
});

Run your project, and noticed it runs the same. Only this time your project can grow gracefully and you are encouraged to use a better design pattern.

Whew! That was a long one for me to write. I really do hope you learned a thing or two from it. Remember this is only the basics and i will be writing a much more advanced tutorial in the future. Feel free to reach out to me regarding this write-up. Be well and happy coding. Feel free to reach out on twitter as well ( @scriptonian ) if you have any questions.

2 Comments

  1. Great article as usual. Very informative. Nice work Kofi!

    1. Thank you Samuel! Appreciate you taking the time to read 🙂 Do check-in from time to time.

Leave a Reply

Your email address will not be published. Required fields are marked *