Christian Vuerings

5 Lessons Learned Using gulp.js

December 29, 2014

Using gulp.js isn’t always straightforward. Here are a couple of tips to help you out.

1. Things don’t run in sequence

With gulp.js tasks don’t run in the order you define them. In fact they run in concurrently to make the total execution faster.

If you however want run tasks in order, you can define dependencies like this:

gulp.task('one', function(cb) {
    cb(err);
});

gulp.task('two', ['one'], function() {

});

gulp.task('default', ['one', 'two']);

In previous versions of gulp, you should have used something like gulp-sequence or run-sequence.

2. Watch carefully

When you watch for changes in files, make sure to only watch the files that change on a regular basis.

// Yes
gulp.watch('src/js/**/*.js', ['js']);

// No
gulp.watch('**/*.*', ['js']);

Otherwise running the gulp task in the command line will result in a slower tasks and high CPU / memory usage.

3. Gulp-inject

After concatenating and minifying CSS and JavaScript files, I wanted to find a way to dynamically add those files to the HTML page. gulp-inject was the plug-in that did the trick for me.

// Make sure all the previous tasks (concatenation / minification) are run first
gulp.task('index', ['images', 'js', 'css', 'fonts'], function() {
    var inject = require('gulp-inject');
    var target = gulp.src('./src/index.html');

    // Read = false will not read the file contents and make the task faster
    var sources = gulp.src([paths.build.js, paths.build.css], {
        read: false
    });

    return target
    .pipe(inject(sources, {

        // Do not add a root slash to the beginning of the path
        addRootSlash: false,

        // Remove the `public` from the path when doing the injection
        ignorePath: 'public'
    }))
    .pipe(gulp.dest('public'));
});

And this is the html part:

<body>
    <!-- inject:js -->
    <!-- endinject -->
</body>

Next to html, it also supports jade, jsx, slm and haml.

4. Using bower?

When you’re using bower to manage your dependencies, main-bower-files will come in handy. Otherwise you’ll have to manually define which bower files you want to include.

var mainBowerFiles = require('main-bower-files');

var paths = {
  src: {
    js: mainBowerFiles({

      // Set the base path for your bower components
      base: './bower_components',

      // Only return the JavaScript files
      filter: /.*\.js$/i

    // Concatenate the bower files with your own
    }).concat(['src/js/**/*.js']);
  }
}

5. Node + browsersync: possible but cumbersome

One of the most tedious parts was finding a way to make gulp work with nodemon (automatically restart node) and BrowserSync. The gulpfile from Hafiz Ismail was a great start and I made some small changes.

gulp.task('nodemon', function(cb) {
  var nodemon = require('gulp-nodemon');

  // We use this `called` variable to make sure the callback is only executed once
  var called = false;
  return nodemon({
    script: 'server.js',
    watch: ['server.js', 'server/**/*.*']
  })
  .on('start', function onStart() {
    if (!called) {
      cb();
    }
    called = true;
  })
  .on('restart', function onRestart() {

    // Also reload the browsers after a slight delay
    setTimeout(function reload() {
      browserSync.reload({
        stream: false
      });
    }, 500);
  });
});

// Make sure `nodemon` is started before running `browser-sync`.
gulp.task('browser-sync', ['index', 'nodemon'], function() {
  var port = process.env.PORT || 5000;
  browserSync.init({

    // All of the following files will be watched
    files: ['public/**/*.*'],

    // Tells BrowserSync on where the express app is running
    proxy: 'http://localhost:' + port,

    // This port should be different from the express app port
    port: 4000,

    // Which browser should we launch?
    browser: ['google chrome']
  });
});

Conclusion

Hope you all taken something away from this post, if you have any remarks / comments, feel free to contact me on twitter.