Search code examples
javascriptreactjsgulpbundlebabeljs

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 !


Solution

  • 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>
    

    Edit 2

    You should set up Browsersync in your gulp file. That way you can tell Browsersync exactly when something has changed, rather than have it set there waiting.

    gulp.task('serve', ['inject'], function() {
    
        // Start browser sync after "inject" has finished.
        browserSync.init({
            server: {
                baseDir: './dist'
            }
        });
    
        // When the watch is triggered, call "reload". That will call "inject" and then let 
        // Browsersync know a change has occurred.
        gulp.watch('*.js', ['reload']);
    
    });
    
    gulp.task('reload', ['inject'], function() {
        browserSync.reload();
    });
    

    The serve task now compiles all your JavaScript, starts Browsersync and begins watching for JavaScript file changes (example above just watches the root directory, you will likely have to change that to suit your project). Once gulp.watch() sees a change, it will call the reload task, which calls the inject task and then reloads Browsersync.

    reload doesn't do much itself, but notice that it calls inject first. So it will wait for that task to finish before it reloads Browsersync.