Trong Lam Phan Trong Lam Phan - 4 months ago 12
Javascript Question

Versioning bundle file

I have a problem that each time i modify my code js, i have to reload my browser three or four times to make it clear the cache. So it's painful.

I'm looking for a solution like versioning the bundle.js which is generated by gulp. Every time my code is modified, i need create a bundle file with a random number like bundle-1287138.js and the script tag in my index.html needs to be automatically updated.

<script src = "/dist/bundle-1287138.js"></script>


How could i do ?

The following code is my gulpfile.js now:

var gulp = require('gulp');
var fs = require("fs");
var browserify = require("browserify");
var babelify = require("babelify");
var source = require('vinyl-source-stream');
var gutil = require('gulp-util');

gulp.task('es6', function() {
browserify({ debug: true })
.transform(babelify)
.require("./main.js", { entry: true })
.bundle()
.on('error',gutil.log)
.pipe(source('./dist/bundle.js'))
.pipe(gulp.dest('./'));
});

gulp.task('watch',function() {
gulp.watch(['app.jsx', 'main.js', './components/**/*.jsx'], ['es6'])
});

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


Thank you !

UPDATE

As MadScone say, i use BrowserSync and it works great. Only one problem, it has delay (8 - 9 seconds) after saving.

This is my folder structure:

- api
- frontend
- gulpfile.js
- index.js


In index.js, i use BrowserSync:

var express = require('express');
var app = express();
var browserSync = require('browser-sync');

var bs = browserSync({ logSnippet: false });
bs.watch("frontend/dist/bundle.js", function() {
var curr = new Date();
console.log("changed " + curr.getHours() + ":" + curr.getMinutes() + ":" + curr.getSeconds());
bs.reload();
});

app.use(require('connect-browser-sync')(bs));
app.use('/', express.static(path.join(__dirname, 'frontend')));

var server = app.listen(3000, function () {
console.log('Server running at http://127.0.0.1:3000/');
});


And in gulpfile.js, i'm using gulp to build my bundle.js:

var gulp = require('gulp');
var fs = require("fs");
var browserify = require("browserify");
var babelify = require("babelify");
var source = require('vinyl-source-stream');
var gutil = require('gulp-util');

gulp.task('es6', function() {
browserify({ debug: true })
.transform(babelify)
.require("./main.js", { entry: true })
.bundle()
.on('error',gutil.log)
.pipe(source('./dist/bundle.js'))
.pipe(gulp.dest('./'));
});

gulp.task('watch',function() {
gulp.watch(['app.jsx', 'main.js', './components/**/*.jsx'], ['es6'])
});

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


When i modify and save my code, the gulp es6 task regenerates the bundle.js and make the following log:

[23:49:54] Starting 'es6'...
[23:49:54] Finished 'es6' after 6 ms


Because the bundle.js file is changed, so BrowserSync makes its work but 5 seconds later...

changed 23:49:59
[BS] Reloading Browsers...


Could you please explain why it didn't reload right after the es6 task.

Thank you !

Answer

If you want to reload your browser every time your bundle changes, you should really look into Browsersync, which is a much better way of doing this. However, there's a solution below if you don't want to use Browsersync for some reason.

You can use something like node-randomstring to generate the file name (or there are lots of other ways to do that) and then use gulp-inject to inject the bundle into your HTML.

Run the task inject to produce the output you want.

var gulp         = require('gulp');
var fs           = require('fs');
var browserify   = require('browserify');
var babelify     = require('babelify');
var source       = require('vinyl-source-stream');
var gutil        = require('gulp-util');
var path         = require('path');
var inject       = require('gulp-inject');
var del          = require('del');
var randomString = require('randomstring');

var BUILD_DIRECTORY = 'dist';

gulp.task('es6', function() {

    // Create random file name.
    var hash = randomString.generate(8);
    var name = path.join(BUILD_DIRECTORY, 'bundle-' + hash + '.js');

    return browserify({ debug: true })
        .transform(babelify)
        .require('./main.js', { entry: true })
        .bundle()
        .on('error',gutil.log)
        .pipe(source(name))
        .pipe(gulp.dest('./'));

});

// Inject bundle into HTML.
gulp.task('inject', ['clean', 'es6'], function() {

    const target = gulp.src('index.html');
    const sources = gulp.src(path.join(BUILD_DIRECTORY, '*.js'), {read: false});
    return target.pipe(inject(sources))
        .pipe(gulp.dest(BUILD_DIRECTORY));

});

// Delete the old bundle.
gulp.task('clean', function () {
    return del(path.join(BUILD_DIRECTORY, 'bundle*.js'));
});

A new bundle file is created every time you run inject. The clean task deletes any old bundle files, otherwise you will end up with loads of them.

Note that I've assumed your index.html and main.js are at the root of your directory.

Edit

Sorry forgot the HTML:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

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