Browserify hello world

First step, you’ll need to install using npm (the nodejs package manager)

Browsers don’t have the require method defined, but Node.js does. With Browserify you can write code that uses require in the same way that you would use it in Node.

Browserify parses the AST for require() calls to traverse the entire dependency graph of your project.
Drop a single


Bonus: if you put your script tag right before the , you can use all of the dom elements on the page without waiting for a dom onready event.

There are many more things you can do with bundling. Check out the bundling section elsewhere in this document.

how browserify works

Browserify starts at the entry point files that you give it and searches for any require() calls it finds using static analysis of the source code's abstract syntax tree.

For every require() call with a string in it, browserify resolves those module strings to file paths and then searches those file paths for require() calls recursively until the entire dependency graph is visited.

Each file is concatenated into a single javascript file with a minimal require() definition that maps the statically-resolved names to internal IDs.

This means that the bundle you generate is completely self-contained and has everything your application needs to work with a pretty negligible overhead.

For more details about how browserify works, check out the compiler pipeline section of this document.

how node_modules works

node has a clever algorithm for resolving modules that is unique among rival platforms.

Instead of resolving packages from an array of system search paths like how $PATH works on the command line, node's mechanism is local by default.

If you require('./foo.js') from /beep/boop/bar.js, node will look for ./foo.js in /beep/boop/foo.js. Paths that start with a ./ or ../ are always local to the file that calls require().

If however you require a non-relative name such as require('xyz') from /beep/boop/foo.js, node searches these paths in order, stopping at the first match and raising an error if nothing is found:

/beep/boop/node_modules/xyz
/beep/node_modules/xyz
/node_modules/xyz
For each xyz directory that exists, node will first look for a xyz/package.json to see if a "main" field exists. The "main" field defines which file should take charge if you require() the directory path.

For example, if /beep/node_modules/xyz is the first match and /beep/node_modules/xyz/package.json has:

{
"name": "xyz",
"version": "1.2.3",
"main": "lib/abc.js"
}
then the exports from /beep/node_modules/xyz/lib/abc.js will be returned by require('xyz').

If there is no package.json or no "main" field, index.js is assumed:

/beep/node_modules/xyz/index.js
If you need to, you can reach into a package to pick out a particular file. For example, to load the lib/clone.js file from the dat package, just do:

var clone = require('dat/lib/clone.js')
The recursive node_modules resolution will find the first dat package up the directory hierarchy, then the lib/clone.js file will be resolved from there. This require('dat/lib/clone.js') approach will work from any location where you can require('dat').

node also has a mechanism for searching an array of paths, but this mechanism is deprecated and you should be using node_modules/ unless you have a very good reason not to.

The great thing about node's algorithm and how npm installs packages is that you can never have a version conflict, unlike most every other platform. npm installs the dependencies of each package into node_modules.

Each library gets its own local node_modules/ directory where its dependencies are stored and each dependency's dependencies has its own node_modules/ directory, recursively all the way down.

This means that packages can successfully use different versions of libraries in the same application, which greatly decreases the coordination overhead necessary to iterate on APIs. This feature is very important for an ecosystem like npm where there is no central authority to manage how packages are published and organized. Everyone may simply publish as they see fit and not worry about how their dependency version choices might impact other dependencies included in the same application.

You can leverage how node_modules/ works to organize your own local application modules too. See the avoiding ../../../../../../.. section for more.

why concatenate

Browserify is a build step that runs on the server. It generates a single bundle file that has everything in it.

Here are some other ways of implementing module systems for the browser and what their strengths and weaknesses are:

window globals

Instead of a module system, each file defines properties on the window global object or develops an internal namespacing scheme.

This approach does not scale well without extreme diligence since each new file needs an additional
and load that html in a browser. The output will be in the debug console which you can open with F12, ctrl-shift-j, or ctrl-shift-k depending on the browser.

This is a bit cumbersome to run our tests in a browser, but you can install the testling command to help. First do:

npm install -g testling
And now just do browserify test/beep.js | testling:

$ browserify test/beep.js | testling

TAP version 13
# beep
ok 1 5*100 === 500

1..1
# tests 1
# pass 1

# ok
testling will launch a real browser headlessly on your system to run the tests.

Now suppose we want to add another file, test/boop.js:

var test = require('tape');
var hundreder = require('../');

test('fraction', function (t) {
t.plan(1);

hundreder(1/20, function (n) {
t.equal(n, 5, '1/20th of 100');
});
});

test('negative', function (t) {
t.plan(1);

hundreder(-3, function (n) {
t.equal(n, -300, 'negative number');
});
});
Here our test has 2 test() blocks. The second test block won't start to execute until the first is completely finished, even though it is asynchronous. You can even nest test blocks by using t.test().

We can run test/boop.js with node directly as with test/beep.js, but if we want to run both tests, there is a minimal command-runner we can use that comes with tape. To get the tape command do:

npm install -g tape
and now you can run:

$ tape test/*.js
TAP version 13
# beep
ok 1 5*100 === 500
# fraction
ok 2 1/20th of 100
# negative
ok 3 negative number

1..3
# tests 3
# pass 3

# ok
and you can just pass test/*.js to browserify to run your tests in the browser:

$ browserify test/* | testling

TAP version 13
# beep
ok 1 5*100 === 500
# fraction
ok 2 1/20th of 100
# negative
ok 3 negative number

1..3
# tests 3
# pass 3

# ok
Putting together all these steps, we can configure package.json with a test script:

{
"name": "hundreder",
"version": "1.0.0",
"main": "index.js",
"devDependencies": {
"tape": "^2.13.1",
"testling": "^1.6.1"
},
"scripts": {
"test": "tape test/*.js",
"test-browser": "browserify test/*.js | testlingify"
}
}
Now you can do npm test to run the tests in node and npm run test-browser to run the tests in the browser. You don't need to worry about installing commands with -g when you use npm run: npm automatically sets up the $PATH for all packages installed locally to the project.

If you have some tests that only run in node and some tests that only run in the browser, you could have subdirectories in test/ such as test/server and test/browser with the tests that run both places just in test/. Then you could just add the relevant directory to the globs:

{
"name": "hundreder",
"version": "1.0.0",
"main": "index.js",
"devDependencies": {
"tape": "^2.13.1",
"testling": "^1.6.1"
},
"scripts": {
"test": "tape test/*.js test/server/*.js",
"test-browser": "browserify test/*.js test/browser/*.js | testling"
}
}
and now server-specific and browser-specific tests will be run in addition to the common tests.

If you want something even slicker, check out prova once you have gotten the basic concepts.

assert

The core assert module is a fine way to write simple tests too, although it can sometimes be tricky to ensure that the correct number of callbacks have fired.

You can solve that problem with tools like macgyver but it is appropriately DIY.

code coverage

coverify

A simple way to check code coverage in browserify is to use the coverify transform.

$ browserify -t coverify test/*.js | node | coverify
or to run your tests in a real browser:

$ browserify -t coverify test/*.js | testling | coverify
coverify works by transforming the source of each package so that each expression is wrapped in a __coverageWrap() function.

Each expression in the program gets a unique ID and the __coverageWrap() function will print COVERED $FILE $ID the first time the expression is executed.

Before the expressions run, coverify prints a COVERAGE $FILE $NODES message to log the expression nodes across the entire file as character ranges.

Here's what the output of a full run looks like:

$ browserify -t coverify test/whatever.js | node
COVERAGE "/home/substack/projects/defined/test/whatever.js" [[14,28],[14,28],[0,29],[41,56],[41,56],[30,57],[95,104],[95,105],[126,146],[126,146],[115,147],[160,194],[160,194],[152,195],[200,217],[200,218],[76,220],[59,221],[59,222]]
COVERED "/home/substack/projects/defined/test/whatever.js" 2
COVERED "/home/substack/projects/defined/test/whatever.js" 1
COVERED "/home/substack/projects/defined/test/whatever.js" 0
COVERAGE "/home/substack/projects/defined/index.js" [[48,49],[55,71],[51,71],[73,76],[92,104],[92,118],[127,139],[120,140],[172,195],[172,196],[0,204],[0,205]]
COVERED "/home/substack/projects/defined/index.js" 11
COVERED "/home/substack/projects/defined/index.js" 10
COVERED "/home/substack/projects/defined/test/whatever.js" 5
COVERED "/home/substack/projects/defined/test/whatever.js" 4
COVERED "/home/substack/projects/defined/test/whatever.js" 3
COVERED "/home/substack/projects/defined/test/whatever.js" 18
COVERED "/home/substack/projects/defined/test/whatever.js" 17
COVERED "/home/substack/projects/defined/test/whatever.js" 16
TAP version 13
# whatever
COVERED "/home/substack/projects/defined/test/whatever.js" 7
COVERED "/home/substack/projects/defined/test/whatever.js" 6
COVERED "/home/substack/projects/defined/test/whatever.js" 10
COVERED "/home/substack/projects/defined/test/whatever.js" 9
COVERED "/home/substack/projects/defined/test/whatever.js" 8
COVERED "/home/substack/projects/defined/test/whatever.js" 13
COVERED "/home/substack/projects/defined/test/whatever.js" 12
COVERED "/home/substack/projects/defined/test/whatever.js" 11
COVERED "/home/substack/projects/defined/index.js" 0
COVERED "/home/substack/projects/defined/index.js" 2
COVERED "/home/substack/projects/defined/index.js" 1
COVERED "/home/substack/projects/defined/index.js" 5
COVERED "/home/substack/projects/defined/index.js" 4
COVERED "/home/substack/projects/defined/index.js" 3
COVERED "/home/substack/projects/defined/index.js" 7
COVERED "/home/substack/projects/defined/index.js" 6
COVERED "/home/substack/projects/defined/test/whatever.js" 15
COVERED "/home/substack/projects/defined/test/whatever.js" 14
ok 1 should be equal

1..1
# tests 1
# pass 1

# ok
These COVERED and COVERAGE statements are just printed on stdout and they can be fed into the coverify command to generate prettier output:

$ browserify -t coverify test/whatever.js | node | coverify
TAP version 13
# whatever
ok 1 should be equal

1..1
# tests 1
# pass 1

# ok

# /home/substack/projects/defined/index.js: line 6, column 9-32

console.log('whatever');
^^^^^^^^^^^^^^^^^^^^^^^^

# coverage: 30/31 (96.77 %)
To include code coverage into your project, you can add an entry into the package.json scripts field:

{
"scripts": {
"test": "tape test/*.js",
"coverage": "browserify -t coverify test/*.js | node | coverify"
}
}
There is also a covert package that simplifies the browserify and coverify setup:

{
"scripts": {
"test": "tape test/*.js",
"coverage": "covert test/*.js"
}
}
To install coverify or covert as a devDependency, run npm install -D coverify or npm install -D covert.

bundling

This section covers bundling in more detail.

Bundling is the step where starting from the entry files, all the source files in the dependency graph are walked and packed into a single output file.

saving bytes

One of the first things you'll want to tweak is how the files that npm installs are placed on disk to avoid duplicates.

When you do a clean install in a directory, npm will ordinarily factor out similar versions into the topmost directory where 2 modules share a dependency. However, as you install more packages, new packages will not be factored out automatically. You can however use the npm dedupe command to factor out packages for an already-installed set of packages in node_modules/. You could also remove node_modules/ and install from scratch again if problems with duplicates persist.

browserify will not include the same exact file twice, but compatible versions may differ slightly. browserify is also not version-aware, it will include the versions of packages exactly as they are laid out in node_modules/ according to the require() algorithm that node uses.

You can use the browserify --list and browserify --deps commands to further inspect which files are being included to scan for duplicates.

standalone

You can generate UMD bundles with --standalone that will work in node, the browser with globals, and AMD environments.

Just add --standalone NAME to your bundle command:

$ browserify foo.js --standalone xyz > bundle.js
This command will export the contents of foo.js under the external module name xyz. If a module system is detected in the host environment, it will be used. Otherwise a window global named xyz will be exported.

You can use dot-syntax to specify a namespace hierarchy:

$ browserify foo.js --standalone foo.bar.baz > bundle.js
If there is already a foo or a foo.bar in the host environment in window global mode, browserify will attach its exports onto those objects. The AMD and module.exports modules will behave the same.

Note however that standalone only works with a single entry or directly-required file.

external bundles

ignoring and excluding

In browserify parlance, "ignore" means: replace the definition of a module with an empty object. "exclude" means: remove a module completely from a dependency graph.

Another way to achieve many of the same goals as ignore and exclude is the "browser" field in package.json, which is covered elsewhere in this document.

ignoring

Ignoring is an optimistic strategy designed to stub in an empty definition for node-specific modules that are only used in some codepaths. For example, if a module requires a library that only works in node but for a specific chunk of the code:

var fs = require('fs');
var path = require('path');
var mkdirp = require('mkdirp');

exports.convert = convert;
function convert (src) {
return src.replace(/beep/g, 'boop');
}

exports.write = function (src, dst, cb) {
fs.readFile(src, function (err, src) {
if (err) return cb(err);
mkdirp(path.dirname(dst), function (err) {
if (err) return cb(err);
var out = convert(src);
fs.writeFile(dst, out, cb);
});
});
};
browserify already "ignores" the 'fs' module by returning an empty object, but the .write() function here won't work in the browser without an extra step like a static analysis transform or a runtime storage fs abstraction.

However, if we really want the convert() function but don't want to see mkdirp in the final bundle, we can ignore mkdirp with b.ignore('mkdirp') or browserify --ignore mkdirp. The code will still work in the browser if we don't call write() because require('mkdirp') won't throw an exception, just return an empty object.

Generally speaking it's not a good idea for modules that are primarily algorithmic (parsers, formatters) to do IO themselves but these tricks can let you use those modules in the browser anyway.

To ignore foo on the command-line do:

browserify --ignore foo
To ignore foo from the api with some bundle instance b do:

b.ignore('foo')
excluding

Another related thing we might want is to completely remove a module from the output so that require('modulename') will fail at runtime. This is useful if we want to split things up into multiple bundles that will defer in a cascade to previously-defined require() definitions.

For example, if we have a vendored standalone bundle for jquery that we don't want to appear in the primary bundle:

$ npm install jquery
$ browserify -r jquery --standalone jquery > jquery-bundle.js
then we want to just require('jquery') in a main.js:

var $ = require('jquery');
$(window).click(function () { document.body.bgColor = 'red' });
defering to the jquery dist bundle so that we can write:



and not have the jquery definition show up in bundle.js, then while compiling the main.js, you can --exclude jquery:

browserify main.js --exclude jquery > bundle.js
To exclude foo on the command-line do:

browserify --exclude foo
To exclude foo from the api with some bundle instance b do:

b.exclude('foo')
browserify cdn

shimming

Unfortunately, some packages are not written with node-style commonjs exports. For modules that export their functionality with globals or AMD, there are packages that can help automatically convert these troublesome packages into something that browserify can understand.

browserify-shim

One way to automatically convert non-commonjs packages is with browserify-shim.

browserify-shim is loaded as a transform and also reads a "browserify-shim" field from package.json.

Suppose we need to use a troublesome third-party library we've placed in ./vendor/foo.js that exports its functionality as a window global called FOO. We can set up our package.json with:

{
"browserify": {
"transform": "browserify-shim"
},
"browserify-shim": {
"./vendor/foo.js": "FOO"
}
}
and now when we require('./vendor/foo.js'), we get the FOO variable that ./vendor/foo.js tried to put into the global scope, but that attempt was shimmed away into an isolated context to prevent global pollution.

We could even use the browser field to make require('foo') work instead of always needing to use a relative path to load ./vendor/foo.js:

{
"browser": {
"foo": "./vendor/foo.js"
},
"browserify": {
"transform": "browserify-shim"
},
"browserify-shim": {
"foo": "FOO"
}
}
Now require('foo') will return the FOO export that ./vendor/foo.js tried to place on the global scope.

partitioning

Most of the time, the default method of bundling where one or more entry files map to a single bundled output file is perfectly adequate, particularly considering that bundling minimizes latency down to a single http request to fetch all the javascript assets.

However, sometimes this initial penalty is too high for parts of a website that are rarely or never used by most visitors such as an admin panel. This partitioning can be accomplished with the technique covered in the ignoring and excluding section, but factoring out shared dependencies manually can be tedious for a large and fluid dependency graph.

Luckily, there are plugins that can automatically factor browserify output into separate bundle payloads.

factor-bundle

factor-bundle splits browserify output into multiple bundle targets based on entry-point. For each entry-point, an entry-specific output file is built. Files that are needed by two or more of the entry files get factored out into a common bundle.

For example, suppose we have 2 pages: /x and /y. Each page has an entry point, x.js for /x and y.js for /y.

We then generate page-specific bundles bundle/x.js and bundle/y.js with bundle/common.js containing the dependencies shared by both x.js and y.js:

browserify x.js y.js -p [ factor-bundle -o bundle/x.js -o bundle/y.js ] \
-o bundle/common.js
Now we can simply put 2 script tags on each page. On /x we would put:



and on page /y we would put:



You could also load the bundles asynchronously with ajax or by inserting a script tag into the page dynamically but factor-bundle only concerns itself with generating the bundles, not with loading them.

partition-bundle

partition-bundle handles splitting output into multiple bundles like factor-bundle, but includes a built-in loader using a special loadjs() function.

partition-bundle takes a json file that maps source files to bundle files:

{
"entry.js": ["./a"],
"common.js": ["./b"],
"common/extra.js": ["./e", "./d"]
}
Then partition-bundle is loaded as a plugin and the mapping file, output directory, and destination url path (required for dynamic loading) are passed in:

browserify -p [ partition-bundle --map mapping.json \
--output output/directory --url directory ]
Now you can add:


to your page to load the entry file. From inside the entry file, you can dynamically load other bundles with a loadjs() function:

a.addEventListener('click', function() {
loadjs(['./e', './d'], function(e, d) {
console.log(e, d);
});
});
compiler pipeline

Since version 5, browserify exposes its compiler pipeline as a labeled-stream-splicer.

This means that transformations can be added or removed directly into the internal pipeline. This pipeline provides a clean interface for advanced customizations such as watching files or factoring bundles from multiple entry points.

For example, we could replace the built-in integer-based labeling mechanism with hashed IDs by first injecting a pass-through transform after the "deps" have been calculated to hash source files. Then we can use the hashes we captured to create our own custom labeler, replacing the built-in "label" transform:

var browserify = require('browserify');
var through = require('through2');
var shasum = require('shasum');

var b = browserify('./main.js');

var hashes = {};
var hasher = through.obj(function (row, enc, next) {
hashes[row.id] = shasum(row.source);
this.push(row);
next();
});
b.pipeline.get('deps').push(hasher);

var labeler = through.obj(function (row, enc, next) {
row.id = hashes[row.id];

Object.keys(row.deps).forEach(function (key) {
row.deps[key] = hashes[row.deps[key]];
});

this.push(row);
next();
});
b.pipeline.get('label').splice(0, 1, labeler);

b.bundle().pipe(process.stdout);
Now instead of getting integers for the IDs in the output format, we get file hashes:

$ node bundle.js
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o bundle.js
would load a plugin called foo. foo is resolved with require(), so to load a local file as a plugin, preface the path with a ./ and to load a plugin from node_modules/foo, just do -p foo.

You can pass options to plugins with square brackets around the entire plugin expression, including the plugin name as the first argument:

$ browserify one.js two.js \
-p [ factor-bundle -o bundle/one.js -o bundle/two.js ] \
> common.js
This command-line syntax is parsed by the subarg package.

To see a list of browserify plugins, browse npm for packages with the keyword "browserify-plugin": http://npmjs.org/browse/keyword/browserify-plugin

authoring plugins

To author a plugin, write a package that exports a single function that will receive a bundle instance and options object as arguments:

// example plugin

module.exports = function (b, opts) {
// ...
}
Plugins operate on the bundle instance b directly by listening for events or splicing transforms into the pipeline. Plugins should not overwrite bundle methods unless they have a very good reason.

Product Description Master the fundamentals of React as you develop applications supported by Flux, React Router, Gulp, and Browserify.

Questions