Asset versioning when using gulp

assetsWhen changing client code, you need to somehow force the client's browser to reload the modified files. This problem is quite common. Most likely, you will encounter it regardless of the technologies you use in web development. This example is about solving the problem when creating a frontend project using JavaScript and Gulp.

You can solve it in several ways.

Let's say, for example, 7e50961489 is the new version of the assets. Actually, it can be any version, the main thing is that it should be different from the previous ones. You can simply number them - 1, 2, 3, etc. But it's not very convenient. Later it will be clear why.

1) You can always change the names of files when updating the application:

<script src="/js/lib-7e50961489.js" type="text/javascript"></script>

2) You can add the version of the assets, for example:

<script src="/js/lib.js?v=7e50961489" type="text/javascript"></script>

3) You can change the file path:

<script src="/js/7e50961489/lib.js" type="text/javascript"></script>

The last option is good because you don't need to change the file name, and the path fragment can be handled through the Nginx configuration. (This way, you don't need to change the file structure).

In this example, let's consider the first option.

In one of the previous notes, we discussed an example of a gulp file configuration for a frontend project. If you haven't worked with Gulp before, be sure to familiarize yourself with it before reading further.

In order to solve the task set at the beginning, we will need three more plugins for Gulp: gulp-rev, gulp-rev-replace, and gulp-rename:

npm install gulp-rev gulp-rev-replace gulp-rename --save

The main work is done by gulp-rev. Its task is to rename asset files according to the version.

As promised, let me explain why the version is not simply 1, 2, 3, etc. The version contains information about the checksum of the files. Therefore, unchanged files are not rebuilt.

So, let's expand the CoffeeScript file build task. First, let's use gulp-rev:

gulp.task('coffee-lib', function () {
    gulp.src(['./js/**/*.coffee', '!./js/app/web.coffee'])
        .pipe(coffee())
        .pipe(concat('lib.js'))
        // add version to the file name
        .pipe(rev())
        .pipe(gulp.dest('./public/js/'))
        // save the version to a file - will be needed later
        .pipe(rev.manifest('rev-manifest-lib.json'))
        .pipe(gulp.dest('./build'))
    ;
});

We have dealt with the asset files themselves. Next, we need the new files to be used in the inclusion places. For this, I created a "template file" index.html.dist, which will be built into index.html with each build, and the latter should not be under version control.

 <!--index.html.dist-->
....
<script src="js/lib.js"></script>
....

We use rev-replace to replace paths to the included files and rename to rename (or rather, copy) index.html.dist to index.html:

gulp.task('template', ['coffee-lib'], function () {
    var manifest = gulp.src([
        './build/rev-manifest-lib.json',
    ]);

    return gulp.src('./public/index.html.dist')
        .pipe(revReplace({
            manifest: manifest,
            replaceInExtensions: ['.dist']
        }))
        // rename the file
        .pipe(rename("index.html"))
        .pipe(gulp.dest('./public'))
    ;
});

Pay attention to the line in the config replaceInExtensions: ['.dist']. By default, rev-replace does not process .dist files.

Now, we just need to add the new ('template') task to the 'default' task (the 'coffee-lib' task should have been added earlier):

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