Gulp

By Jake Chapman February 4, 2014

Quick intro to Gulp.js

Has the time already come for me to hang up the old and put on the new? Let's dig into what Gulp.js is and why I'm considering switching from Grunt.js.

Gulp.js - The streaming build system

Gulp is a Task / Build runner. If you take a look at the Gulp.js site, you'll see some of the things Gulp can bring to the table. It is easy to use, has a simple API, and is efficient.

Gulp.js makes use of pipes for streaming data that needs to be processed. Its syntax for setting up tasks to me is extremely simple compared to a gruntfile. There's also a vast array of plugins out there to make everyday tasks a breeze, like minifying code, uglifying JavaScript, watching files and livereloading the browser when files have changed. First, let's take a quick look at how complicated gruntfile.js is. Later we'll look at how to set up almost the same thing much more simply with Gulp.

Gruntfile.js

     module.exports = function(grunt) {
      // load all grunt tasks
      require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);

      grunt.initConfig({
        uglify: {
          dist: {
            src: ['assets/js/vendor/jquery.js', 'assets/js/vendor/**/*.js'],
            dest: "assets/js/vendor.min.js"
          }
        },
        sass: {
          options: {
            style: "expanded",
            lineNumbers: true
          },
          dist: {
            files: { 'assets/styles/application.css': 'assets/styles/application.scss' }
          }
        },
        watch: {
          scripts: {
            files: ['assets/js/**/*.js', '!assets/js/vendor.min.js'],
            tasks: ['uglify'],
            options: {
                spawn: false,
            },
          },
          css: {
            files: ['assets/styles/**/*.scss'],
            tasks: ['sass'],
            options: {
                spawn: false,
            }
          }
        }
      });

      grunt.registerTask('default', ['uglify', 'sass']);
    };

package.json

 {
      "name": "PACKAGE NAME",
      "version": "PACKAGE VERSION",
      "devDependencies": {
        "grunt": "~0.4.2",
        "grunt-contrib-sass": "~0.5.1",
        "grunt-contrib-watch": "~0.4.3",
        "grunt-contrib-uglify": "~0.2.0",
        "matchdep": "~0.1.2"
      }
    }

This simple Gruntfile comes in at around 50 lines of code. Now let's take a look at Gulp. You'll notice how much cleaner the syntax is. It mimics the Node.js syntax way of writing code. The more I'm learning Node, the more and more I'm enjoying it.

First we need to install Gulp globally.

npm install -g gulp

Just like Grunt, we need to create a package.json file at the root of the project directory. Inside of that, add the following

{
  "devDependencies": {}
}

Now we need to install gulp and gulp-utils to our demo project's devDependencies.

npm install gulp gulp-util --save-dev

Again, like Grunt we need to create a file that tells Gulp its tasks, what those tasks are, and when to run them. Let's create a gulpfile.js file at the root of the project directory. This is where the magic happens.

gulpfile.js

    var gulp = require('gulp');
    var gutil = require('gulp-util');

    gulp.task('default', function(){
      // Default task code
    });

Now run gulp from the command line and watch the magic. You should see output similar to this:

[gulp] Using file        /Users/jakechapman/Sites/gulpdemo/gulpfile.js
[gulp] Working directory changed to /Users/jakechapman/Sites/gulpdemo
[gulp] Running 'default'...
[gulp] Finished 'default' in 73 μs

Awesome, Gulp is officially installed, added as a devDependency, and you're ready to rock. Let's continue with our comparing of the gruntfile.js and making Gulp do everything Grunt was already doing. First let's get grunt to deal with our scss. Let's add the Sass plugin to Gulp. From the command line, type:

npm install gulp-ruby-sass --save-dev

Now in our gulpfile.js, we need to require the Sass plugin and then tell Gulp what to do with our files. Here is our updated gulpfile.js:

    var gulp = require('gulp');
    var gutil = require('gulp-util');

    // require sass
    var sass = require('gulp-ruby-sass');

    gulp.task('sass', function () {
      gulp.src('./assets/styles/**/*.scss')
        .pipe(sass())
        .pipe(gulp.dest('./assets/styles'));
    });

    gulp.task('default', ['sass']);

We've just created a task called 'sass' and inside of its anonymous function, told it what to do. We grab the src file(s) we want to process, which in this case is ./assets/styles/*/.scss—notice the */. Like Grunt, the double ** is saying, "Look in all the subfolders within styles and for all of the .scss files." We then pipe the file(s) to the Sass function, then pipe what that returns to a Gulp destination directory that we want that final file to live. In this case, it's gulp.dest('./assets/styles')

After that, we just retold Gulp what we want to run when the default task is ran. Make sure you have the /assets/styles/app.scss file in place and run Gulp again and watch it do its magic.

This is cool and all, but still not everything we want. I'm going to jump forward a bit and have Gulp doing the same type of thing for our JS files and add the code for watching our files. Here is the updated gulpfile.

        var gulp = require('gulp'),
        gutil = require('gulp-util'),
        sass = require('gulp-ruby-sass'),
        uglify = require('gulp-uglify'),
        watch = require('gulp-watch'),
        concat = require('gulp-concat'),
        notify = require('gulp-notify');

    // sass task
    gulp.task('sass', function () {
      gulp.src('./assets/styles/**/*.scss')
        .pipe(sass({ 
          noCache: true,
          style: "expanded",
          lineNumbers: true,
          loadPath: './assets/styles/*'
        }))
        .pipe(gulp.dest('./assets/styles'))
        .pipe(notify({
          message: "You just got super Sassy!"
        }));;
    });

    // uglify task
    gulp.task('js', function() {
      // main app js file
      gulp.src('./assets/js/app.js')
      .pipe(uglify())
      .pipe(concat("app.min.js"))
      .pipe(gulp.dest('./assets/js/'));

      // create 1 vendor.js file from all vendor plugin code
      gulp.src('./assets/js/vendor/**/*.js')
      .pipe(uglify())
      .pipe(concat("vendor.js"))
      .pipe(gulp.dest('./assets/js'))
      .pipe( notify({ message: "Javascript is now ugly!"}) );
    });

    gulp.task('watch', function() {
      // watch scss files
      gulp.watch('./assets/styles/**/*.scss', function() {
        gulp.run('sass');
      });

      gulp.watch('./assets/js/**/*.js', function() {
        gulp.run('js');
      });
    });

gulp.task('default', ['sass', 'js', 'watch']);

{
       "devDependencies": {
        "gulp-util": "~2.2.12",
        "gulp": "~3.4.0",
        "gulp-uglify": "~0.1.0",
        "gulp-watch": "~0.5.0",
        "gulp-concat": "~2.1.7",
        "gulp-ruby-sass": "~0.2.0",
        "gulp-notify": "~0.3.4-1"
      }
    }

You can see that I've added some file watching, which reruns the appropriate task and then also added a notify plugin that pushes notifications to the Mac notification center and let's me know when tasks are completed.

I'm still new to Gulp and for now this works for my needs, but I can already tell there will be some things I'll need to modify to work under specific situations.

Here are two things I'd love to see in Gulp.js:

  • Sending error messages to notification center when I mess up in my scss files.

  • Continue with the task after an error has be emitted.


Get job-ready in JavaScript »


This post originally appeared on Jake Chapman's blog.


Code 401: Advanced Software Development in Full-Stack JavaScript

Build full-stack web applications as you learn the core JavaScript language, plus the top frameworks and libraries to create full-stack, feature-rich user experiences.

Learn More