Automating your build tasks easily with Gulp.js

01 November, 2015
post-banner

There are many tasks in web development that are repetitive and can get very boring. Luckily there are build tools which can automate many of these repetitive tasks for you and make your development process a whole lot more efficient. Gulp is a JavaScript task runner which allows developers to efficiently automate reoccurring tasks and speed up workflow.

What is Gulp?

Gulp is built on Node.js and uses streams, pipes and asynchronous code to give you fast and efficient builds. There is also a great range of plugins available, that are simple to use and work just as you would expect them to.

Gulp-site

But what about Grunt?

Grunt is probably the most popular alternative to Gulp, as it is very similar in its purpose. Grunt is also a JavaScript task runner, but the way you configure tasks is different. Grunt tasks are usually determined by using JSON configurations, whereas Gulp is completely written in JavaScript. This, personally, is the main reason why I prefer using Gulp for automating tasks in my projects, as it makes the code you write more familiar.

Another difference is that Gulp runs tasks as concurrently as possible, which means that the tasks are run alongside each other. Grunt is run in a sequential manner, and this can sometimes be slower. Another thing Gulp is great at is that it pipes data from one task to another. which means faster processing and more flexibility with the tasks.

There are several other differences between the two, but ultimately they set out to achieve very similar results. It's a matter of preference.

Using Gulp build tools

Gulp is flexible when it comes to the folder structure of your project, so once you understand the flow of Gulp build tasks you are able to tweak it for a specific project.

The list of tasks we'll setup today are:

  • Compiling Sass files into CSS
  • Minifying CSS, HTML, JavaScript and image files into the build directory
  • Run JS Hint to debug your JavaScript
  • Retrieve the project file size
  • Run a server for the application

Installation and Setup

Gulp needs to be installed globally on your machine. this can be done using npm:

$ npm install -g gulp

So, lets create a new project and cd into it.

$ mkdir gulp-project && cd gulp-project

Next run the npm init command to create a package.json file for the project. This will prompt you with several questions regarding the information for the project.

$ npm init

You then need to install gulp into your project directory. If you run --save-dev alongside the npm command it will automatically save it to your package.json dev dependencies.

$ npm install --save-dev gulp

Next you can create a gulpfile.js at the root of your project directory. To get started just add a simple default task to the gulpfile, and make sure to 'require' gulp.

var gulp = require('gulp');

gulp.task('default', function() {  
  // This is where the code for the task should go
});

This simple code will allow you to run the gulpfile in your terminal using the gulp command. Gulp should run, do nothing and then quit because no tasks were defined in the default task function.

$ gulp
[17:33:41] Using gulpfile ~/projects/gulp-article/gulpfile.js
[17:33:41] Starting 'default'...
[17:33:41] Finished 'default' after 173 Ξs

Automating tasks with the gulpfile

For this tutorial, let's assume that you have a somewhat equivalent directory setup as follows:

├── app/
│   ├── css/
│   ├── images/
│   ├── index.html
│   ├── sass/
│   └── scripts/
├── build/
├── gulpfile.js
├── node_modules/
│   └── gulp/
└── package.json

To start with we will have a basic setup in the app folder which will hold our index.html, sass, css, scripts and images directories. Another empty folder, build, is created in the root which will be where we compile our minified project. The node_modules folder is where all of the installed Gulp plugins will live and the package.json is where they are determined as dependencies for the project. The gulpfile.js is also created on the root and is where we'll be spending most of our time.

Compiling Sass files into minified CSS

Sass is a CSS pre-processor and is a powerful tool for implementing responsive styling to your applications. Browsers are currently not able to compile Sass on their own, and writing watch commands every time we edit stylesheets can get annoying.

Gulp makes this simple and with a few lines of JavaScript we are able to compile all of our Sass files into one compiled CSS file, which can then be minified for production.

There are a couple of dependencies we will need to install locally for this task. They are:

$ npm install --save-dev gulp-sass
$ npm install --save-dev gulp-minify-css

The dependencies will automatically be added to the package,json and are ready to be used. We then need to add them to the top of the gulpfile, below where we required gulp itself.

// Include Gulp plugins
var sass = require('gulp-sass');  
var minifyCSS = require('gulp-minify-css');  

Now we can create the task function to compile the Sass and minify the compiled CSS. If you read the following code step-by-step it's pretty easy to follow what is happening.

// Task to compile Sass file into CSS, and minify CSS into build directory
gulp.task('styles', function() {  
  gulp.src('./app/sass/styles.scss')
    .pipe(sass().on('error', sass.logError))
    .pipe(gulp.dest('./app/css'))
    .pipe(minifyCSS())
    .pipe(gulp.dest('./build/styles/'));
});

The gulp task is given a name of 'styles', which we can use to call the task in the terminal. The source of the Sass files is then set so the task knows where the files we want to compile are coming from. Next the any errors in the code will be logged to the terminal, which is great for debugging the Sass file. The destination for the CSS is also set, and the minifyCSS() function is called to create the build version of the CSS>

Now if we run the task, we get the following output from the terminal:

$ gulp styles
[10:11:52] Using gulpfile ~/projects/gulp-article/gulpfile.js
[10:11:52] Starting 'styles'...
[10:11:52] Finished 'styles' after 10 ms

This means that there were no errors in the code and the task was run successfully. If you look inside the app/ and build/ directories you will see the compiling and minification has worked successfully.

Minify JavaScript, HTML and image files into build

Compressing files in an application can be important, especially on larger applications where the file size could affect the run time. Gulp makes this really easy and there are several plugins available to compress all types of files. Install the following plugins to minify HTML, JavaScript and image files in your project.

$ npm install gulp-minify-html --save-dev
$ npm install gulp-concant --save-dev
$ npm install gulp-strip-debug --save-dev
$ npm install gulp-uglify --save--dev
$ npm install gulp-imagemin --save-dev

So, gulp-minify-html and gulp-imagemin are packages to minimize HTML and image files. gulp-concat is used to concatenate JavaScript files together, gulp-strip-debug will strip console and debugging statements from the JS code and gulp-uglify will then minify the JS.

// Minification dependencies
var minifyHTML = require('gulp-minify-html'); // Minify HTML  
var concat = require('gulp-concat'); // Join all JS files together to save space  
var stripDebug = require('gulp-strip-debug'); // Remove debugging stuffs  
var uglify = require('gulp-uglify'); // Minify JavaScript  
var imagemin = require('gulp-imagemin'); // Minify images  

The plugins can then be added to the top of the gulpfile along with the other plugin dependencies.

Let's start with the HTML. All we need to do to minfy the HTML is to add a new task, set the source of the files we need, call the minifyHTML() plugin and finally set the destination for the changes.

// Task to minify new or changed HTML pages
gulp.task('html', function() {  
  return gulp.src('./app/*.html')
    .pipe(minifyHTML())
    .pipe(gulp.dest('./build/'));
});

The image minification is done in much the same way. You can set the new task, set the source, then call the imagemin() function and set the destination. With this task there are several more options you can select. Setting progressive to true will add lossless compression for JPG files. You can check out some others at the npm page for the plugin.

// Task to minify images into build
gulp.task('images', function() {  
  gulp.src('./app/images/*')
  .pipe(imagemin({
    progressive: true,
  }))
  .pipe(gulp.dest('./build/images'));
});

Now for the JavaScript. There are a few plugins we are going to use for this, but each of them is very useful and simple to use. Again we add the task, set the source, call the functions and set the destination through the Node streaming method. Calling concat() will concatenate all the JavaScript files into one file that here we've called script.js. Calling stripDebug() will remove all console.log and other debugging statements from the code as these aren't needed in production. And finally, calling uglify() will minify the JavaScript into the single, pre-defined script file.

// Task to concat, strip debugging and minify JS files
gulp.task('scripts', function() {  
  gulp.src(['./app/scripts/lib.js', './app/scripts/*.js'])
    .pipe(concat('script.js'))
    .pipe(stripDebug())
    .pipe(uglify())
    .pipe(gulp.dest('./build/scripts/'));
});

Now you are able to run $ gulp html, $ gulp images or $ gulp scripts in the terminal to run these tasks on the files in your app/ directory. If you then check the build/ directory the output should be as you would expect.

Run JS Hint to debug your JavaScript files

Debugging JavaScript using the browser developer tools is not everyone's cup of tea. Fortunately there is a Gulp task (obviously) that will allow us to debug our JavaScript in the terminal when we try to execute the application. Let's install gulp-jshint using npm.

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

Again we need to require the plugin at the top of the gulpfile along with the other plugins.

var jshint = require('gulp-jshint'); // Debug JS files  
var stylish = require('jshint-stylish'); // More stylish debugging  

And again we can create the new task, set the source location and call jshint(). For this task we don't need to specify a destination because the only output will be the JS lint in the terminal.

// Task to run JS hint
gulp.task('jshint', function() {  
  gulp.src('./app/scripts/*.js')
    .pipe(jshint())
    .pipe(jshint.reporter('default'));
});

Now if you run $ gulp jshint in the terminal you will see the errors in your code displayed to you (if there are any). We can go one step further and change the 'reporter' style for the linter. If we install a package called jshint-stylish we can get a fancier output for this task. To do this all you need to do is change the jshint.reporter('default') value to jshint-stylish like this:

// Task to run JS hint (with some style)
gulp.task('jshint', function() {  
  gulp.src('./app/scripts/*.js')
    .pipe(jshint())
    .pipe(jshint.reporter('jshint-stylish'));
});

And now running $ gulp jshint will give us a much nicer display of all of our errors.

Retrieve the size of the project

Now after all the effort we've gone through to compile and compress our project files, it's a good idea to check how much file space we are actually saving. There is a gulp plugin that allows you to do just that.

$ npm install gulp-size --save-dev

Again let's install the plugin and add it to the dependencies.

var size = require('gulp-size'); // Get the size of the project  

We can create two tasks here, one to check the size of the initial application, and one to check the size of our application in production.

Let's create the tasks, set the source for the directories we want to check and we can set showFiles() to true to check the sizes of each individual file.

// Task to get the size of the app project
gulp.task('size', function() {  
  gulp.src('./app/**')
    .pipe(size({
    showFiles: true,
  }));
});

// Task to get the size of the build project
gulp.task('build-size', function() {  
  gulp.src('./build/**')
  .pipe(size({
    showFiles: true,
  }));
});

Now if you run $ gulp size or $ gulp build-size in the terminal you will be displayed with the sizes for each of the directories.

Serving your application

Now finally we want to be able to run our application on a server and for the browser to automatically reload every time we make a change to any file. Let's install browser-sync and add it to the gulpfile to start this off.

$ npm install browser-sync --save-dev
var browserSync = require('browser-sync'); // Reload the browser on file changes  

Now we can create a task called serve which takes in all of the other tasks we've created as parameters so that they can be run concurrently with the serve task. We can also initialise browser-sync to run a server for the application we have set.

// Serve application
gulp.task('serve', ['styles', 'html', 'scripts', 'images', 'jshint', 'size'], function() {  
  browserSync.init({
    server: {
      baseDir: 'app',
    },
  });
});

Now we can add watch tasks to the default task from before. This task should be at the bottom of the gulpfile (just to keep things semantic). Here we define the new task and pass in the serve and styles task. We must pass in styles because the browser reload works in a different way for this task.

We set gulp to 'watch' the source locations, and call the browserSync.reload function.

// Run all Gulp tasks and serve application
gulp.task('default', ['serve', 'styles'], function() {  
  gulp.watch('app/sass/**/*.scss', ['styles']);
  gulp.watch('app/*.html', browserSync.reload);
  gulp.watch('app/scripts/**/*.js', browserSync.reload);
});

Finally we must change the initial styles task to allow this to work. All this requires is to add another 'pipe' to the bottom of the task.

.pipe(browserSync.reload({
  stream: true,
}));

After all this work (trust me, it's worth it) we can finally just run $ gulp in the terminal to automate all of the tasks we have defined. We only have to run this once and Gulp will automatically reload the browser and display any relevant errors messages in the terminal.

Fin

Here you can find a GitHub Gist of my gulpfile from the article.

As you can see, Gulp is a very powerful (but simple) tool for small and large applications and I hope this article was helpful in understanding how to use Gulp tasks to automate your workflow more efficiently.

Go ahead and check out the Gulp plugin documentation for a vast amount more useful plugins for your gulpfile.

comments powered by Disqus