Versioning Static Files with gulp.js

by
26th April 2016

You may find yourself working on a project, making changes on your Javascript files, stylesheets and communicating your scripts with a backend. However, as it is recommended, to set static files headers to expire in a long time to reduce load time on your site, your changes will not get reflected in the clients’ browser that already have your assets cached.
You will need some way to handle this situation. This is where gulp.js can help!

Gulp.js is a npm package that lets you create tasks which can help build an automation system and improve your web development workflow. Those tasks can represent CSS preprocessing, code minification, concatenate files and many others.

In order to install Gulp, you just type on terminal,

$ sudo npm install -g gulp

All your tasks should be placed inside gulpfile.js
There’s a big community around it and several plugins you can use to achieve common tasks.

A basic task in gulp.js

Basically, Gulp works as a pipeline in which for each task you set a source directory or files, pipe those through processes or gulp plugins and output them in a destination directory.
This is a simple example,

 var gulp = require('gulp')
 var uglify = require('gulp-uglify')
 var concat = require('gulp-concat')
 var rename = require('gulp-rename')
 var static_url = ‘/static/’;
 var paths = {
   js: {
     src: static_url + ‘/src/js/**/*.js',
     build: static_url + '/build/js'
   }
 }
 gulp.task('js', function() {
  return gulp.src(paths.js.src)
   .pipe(concat('all.js'))
   .pipe(gulp.dest(paths.js.build))
   .pipe(rename({ suffix: '.min' }))
   .pipe(uglify())
   .pipe(gulp.dest(paths.js.build))
 })

In the above example, we first select the source for Javascript files (it can be a glob), then concatenate all js files into “all.js”, save them, and finally save a minified one named “all.min.js”.

The Manual Way

Now, there are many ways to tell the browser to refresh cache on the files you need to update. If we rename a file, then it will be considered as a new file by the browser and we’ll succeed. However, an elegant and easier way to achieve that is by just appending a parameter next to the file, i.e “/path/to/file.js?v=1.0”, and every time we make changes on that file, remember to update its reference on HTML.

That’s not bad, but you’ll forget about it! And maybe when working with several Git branches, you’ll lose track of the number.

Creating a Task

Instead, we can use Gulp. gulp-rev is a static asset revisioner that appends content hash to filenames, and generates a manifest.json file which can be used to map the asset filenames with the hashed ones. However, if you just need to append a hash parameter next to your assets references you can try with gulp-rev-qs.

$ npm install --save-dev gulp-rev-qs

Here’s a use case example in a Django project that follows the above example.


 var rev = require('gulp-rev-qs');
 gulp.task('build', ['js'], function() {
   return gulp.src('./templates/index.html')
     .pipe(rev({
        base: ‘./’,
        resolver: function(assetPath) {
          return assetPath.replace(‘{{STATIC_URL}}’, static_url)
        }
      }))
     .pipe(gulp.dest('./templates')) /* where we write index.html */
 })

gulp-rev-qs matches any asset path containing the “?rev=” parameter inside your HTML (index.html in the example above) and replaces its value with its MD5 asset hash, so each change on your assets will be automatically reflected in their hash.

Here, we’ve also replaced Django’s {{STATIC_URL}} with the actual static folder at runtime, because we need to read those files in order to calculate their hashes.

It’s worth saying that If you’re using Django 1.7 or later in your project you would better consider taking a look at ManifestStaticFilesStorage storage backend, which works like gulp-rev but Django handles the manifest file to reference the right asset files.