Introduction to Gulp.js 5: Bundling JavaScript with Browserify
This is the 5th part of my series, Introduction to Gulp.js. Today I will show how to use Browserify to bundle your JavaScript and use CommonJS modules to run node modules in the Browser.
Browserify
This task is more complex because I use Browserify to bundle my JavaScript. If this is too complex for your needs, you may use gulp-concat to concatenate all your JavaScript files into one file.
Browserify is a wonderful tool, which allows you to use node modules in your browser. Over 70% of the node modules will run! And it will bundle up all of your dependencies. If you want to find out more about writing CommonJS modules for Browserify, have a look at the documentation.
This task I saw in the gulp-starter blendid. It’s long but clever. It allows the creation of multiple files with Browserify. I create two files. One file is loaded in the head of my website containing Modernizr and one file with the rest of my JavaScript at the bottom.
Creating JavaScript files with Browserify
Install the node modules needed for this task:
$ npm install --save-dev browserify@11.2.0 vinyl-source-stream@1.0.0 watchify@3.4.0 gulp-util@3.0.1 pretty-hrtime@1.0.1 gulp-notify@2.0.0
Create the entry in the config.js
file:
gulp/config.js
browserify: {
// Enable source maps
debug: true,
// Additional file extensions to make optional
extensions: ['.coffee', '.hbs'],
// A separate bundle will be generated for each
// bundle config in the list below
bundleConfigs: [{
entries: './' + srcAssets + '/javascripts/application.js',
dest: developmentAssets + '/js',
outputName: 'application.js'
}, {
entries: './' + srcAssets + '/javascripts/head.js',
dest: developmentAssets + '/js',
outputName: 'head.js'
}]
}
gulp/tasks/development/scripts.js
var gulp = require("gulp");
var browsersync = require("browser-sync");
var browserify = require("browserify");
var source = require("vinyl-source-stream");
var watchify = require("watchify");
var bundleLogger = require("../../util/bundleLogger");
var handleErrors = require("../../util/handleErrors");
var config = require("../../config").browserify;
/**
* Run JavaScript through Browserify
*/
gulp.task("scripts", function (callback) {
browsersync.notify("Compiling JavaScript");
var bundleQueue = config.bundleConfigs.length;
var browserifyThis = function (bundleConfig) {
var bundler = browserify({
// Required watchify args
cache: {},
packageCache: {},
fullPaths: false,
// Specify the entry point of your app
entries: bundleConfig.entries,
// Add file extensions to make optional in your requires
extensions: config.extensions,
// Enable source maps!
debug: config.debug,
});
var bundle = function () {
// Log when bundling starts
bundleLogger.start(bundleConfig.outputName);
return (
bundler
.bundle()
// Report compile errors
.on("error", handleErrors)
// Use vinyl-source-stream to make the
// stream gulp compatible. Specify the
// desired output filename here.
.pipe(source(bundleConfig.outputName))
// Specify the output destination
.pipe(gulp.dest(bundleConfig.dest))
.on("end", reportFinished)
);
};
if (global.isWatching) {
// Wrap with watchify and rebundle on changes
bundler = watchify(bundler);
// Rebundle on update
bundler.on("update", bundle);
}
var reportFinished = function () {
// Log when bundling completes
bundleLogger.end(bundleConfig.outputName);
if (bundleQueue) {
bundleQueue--;
if (bundleQueue === 0) {
// If queue is empty, tell gulp the task is complete.
// https://github.com/gulpjs/gulp/blob/master/docs/API.md#accept-a-callback
callback();
}
}
};
return bundle();
};
// Start bundling with Browserify for each bundleConfig specified
config.bundleConfigs.forEach(browserifyThis);
});
This task has additional utilities for handling errors and logging the bundling process. Put these into a util
folder in your gulp
folder:
gulp/util/bundleLogger.js
/* bundleLogger
------------
Provides gulp style logs to the bundle method in browserify.js
*/
var gutil = require("gulp-util");
var prettyHrtime = require("pretty-hrtime");
var startTime;
module.exports = {
start: function (filepath) {
startTime = process.hrtime();
gutil.log("Bundling", gutil.colors.green(filepath));
},
end: function (filepath) {
var taskTime = process.hrtime(startTime);
var prettyTime = prettyHrtime(taskTime);
gutil.log("Bundled", gutil.colors.green(filepath), "in", gutil.colors.magenta(prettyTime));
},
};
gulp/util/handleErrors.js
var notify = require("gulp-notify");
module.exports = function () {
var args = Array.prototype.slice.call(arguments);
// Send error to notification center with gulp-notify
notify
.onError({
title: "Compile Error",
message: "<%= error.message %>",
})
.apply(this, args);
// Keep gulp from hanging on to this task
this.emit("end");
};
Using CommonJS Modules
Writing CommonJS modules is nice. You export your function, object, string, or integer, you like to export as a module or individually:
math.js
exports.add = function() {
var sum = 0, i = 0, args = arguments, 1 = args.length;
while (i < 1) {
sum += args[i++];
}
return sum;
};
navigation.js
module.exports = {
toggleNavigation: function() {
...
}
};
Later, you import your modules and use them:
increment.js
var add = require("./math").add;
exports.increment = function (val) {
return add(val, 1);
};
application.js
var navigation = require("./navigation");
var triggerNavigation = document.querySelector(".toggle-navigation");
document.addEventListener("DOMContentLoaded", function () {
triggerNavigation.addEventListener("click", navigation.toggleNavigation);
});
Loading non-CommonJS files
But one problem remains: How do I use JavaScript files, which aren’t written in CommonJS syntax? Like Modernizr or jQuery?
I need to install browserify-shim
:
$ npm install --save-dev browserify-shim@3.8.0
I open my package.json
file and need to add a few lines:
package.json
{
"...": "...",
"browser": {
"modernizr": "./app/_bower_components/modernizr/modernizr.js",
"jquery": "./app/_bower_components/jquery/dist/jquery.js"
},
"browserify-shim": {
"modernizr": "Modernizr",
"jquery": "$"
},
"browserify": {
"transform": ["browserify-shim"]
},
"devDependencies": {
"...": "..."
}
}
In the section "browser"
you point browserify-shim
to the asset you want to shim. I use Bower and have installed my packages into app/_bower_components/
. The name you choose is the name you have to require later in your JavaScript.
Within "browerify-shim"
you decide where to map this require
to. To include jQuery or Modernizr later you would write:
app/assets/javascripts/head.js
require("modernizr");
app/_assets/javascripts/application.js
require("jquery");
$(function () {
console.log("jQuery and Modernizr loaded");
});
You have to run npm install
once you added a new entry to your package.json
file.
Conclusion
This concludes the 5th part of my series, Introduction to Gulp.js. We learned how to use Browserify to bundle JavaScript files, how to use CommonJS modules to run Node in your Browser, and how to use non-CommonJS JavaScript files.