Embed URL

HTTPS clone URL

Subversion checkout URL

Browserify has a new website! This inaugural post details the new direction that
browserify has taking in the just-released version 2.0 and the rationale behind
each change.

static resolution

The biggest change in v2 is a move from dynamic module resolution inside the
browser to pre-computed module resolution tables. Switching to static module
resolution means browserify won't need to send its require() resolution
algorithm down the wire. Instead each bundled file has a dependency map
that resolves require() strings to internal module IDs.

For an example of how small the footprint is now,
browser-pack,
the module browserify v2 uses for packing dependency data into a single bundle
has a footprint of just 196 bytes:

$ echo [] | browser-pack | wc -c
196

There is some additional ceremony around each file to define
module, exports, __filename, and __dirname,
but this decoration is only added as necessary by
insert-module-globals
which uses lexical-scope
to analyze the scope variables in the AST.

external require

Static module resolution fixes a lot of v1 bugs for the
browserify -r foo > bundle.js
use case where people just want to generate a script tag that exports a
require() function by use in other scripts.
v1 had problems when the rebasing to remove system-specific paths would
accidentally factor out too much of the path such that browser-side, the path
for something like -r dnode would show up as just /browser.js since all the
files would be based out of node_modules/dnode. v2 fixes this problem by
collapsing all internal module names to integers and statically resolving
require strings to integers on a per-module basis.

For instance a file that does require('foo') and require('./lib/bar.js')
might generate a dependency map like:

{ "foo": 2, "./lib/bar.js": 5 }

Then all the require() defined in module-scope needs to define is

Modules that are intended to be used from outside the bundle by using -r are
just given a string id instead of an integer. Much simpler than before!

multi-bundle fallbacks

Using static resolution with a minimal prelude makes using browserify for
multi-file bundles
where file definitions span multiple files much more feasible.

Splitting bundles up into multiple files can be useful for factoring out common
dependencies in multi-page applications to save on page load times or to factor
out infrequently-changing pieces to exploit browser caching more effectively.

The require() algorithm in v2 supports a fallback routine where it will call
whatever require() was already defined if the current require definition can't
find the relevant module in its local set of files.

more modules

In v2 there's a continued emphasis on splitting out functionality into tiny
reusable modules that each do just one thing and can stand on their own
independently of browserify.

Through v1,
detective,
resolve,
deputy,
and
commondir
have been spun out or grew alongside browserify core. Despite their narrow
scope, these modules have been used as dependencies by other modules aside from
browserify.

New in v2, the job of dependency data gathering has been delegated to
module-deps,
which uses
required
to build the dependency graph.
At the other end,
browser-pack
turns the dependency data into a bundle.
In the middle,
insert-module-globals
takes care of the detecting and defining globals that node modules expect
using lexical-scope to statically
analyze the AST.

more unix

The great thing about all these tiny modules in v2 is that browserify has become
a thin wrapper around this basic pipeline:

You can actually execute this command and it can do most of what browserify
offers! Just run npm install -g module-deps insert-module-globals
browser-pack.

The core browserify library is just 180 sloc and it should now be very easy for
anybody to come along and write their own browserify using the resuable
components published to npm as a starting point.

When it's very easy to disagree and create your own fork, everybody can get a
much better feel for what the solution landscape actually looks like. By
encouraging experimentation, solutions should start to converge more around the
natural trade-offs imposed by the fundamental fabric of computation and less
around the fleeting sway of institutional backing and fashionable APIs.

One of my goals in starting browserify was to bring this kind of
radical reuse
that I had grown used to working with node and npm to the browser.
In v2, browserify can finally champion these very ideals in its own source
code.

asynchronous

Browserify v1 does synchronous i/o for a number of historical reasons, but this
can now be avoided more easily.
required does asynchronous i/o,
and resolve added an async api in
0.3 so browserify v2 can be entirely asynchronous with the streaming api that it
picks up from browser-pack.

An async browserify means that if you want to generate bundles programmatically
in your web server for development or if you want write a browserify build
server, you won't need to worry about synchronous calls clogging up your event
loop.

browser field

In prior releases, browserify would look at the package.json for a
"browserify" field to define a browser-specific version of the "main".

For backwards compatibility the "browserify" field still works but has been
deprecated in favor of the "browser" field.
Most modules shouldn't need a "browser" field at all but it's useful to have
when the same module provides its functionality to node and browsers in
different ways without resorting to #ifdef-esque hacks.

cut features

Part of making v2 more modular and focused involved cutting a lot of features.

Most of the API surface area has been eliminated. The API docs used to be so big
that they had a separate markdown file. Now the
whole API
fits on a single screen in the readme.

One of the worst ideas in browserify, the ad-hoc http server middleware to host
bundles is finally gone.

Default support for coffee-script is gone. You can still use coffee script in
your program, you'll just need to either compile to js or hook the
source transformation into the bundle pipeline yourself.

Remember that if you disagree with these cuts which I expect many people will,
with the v2 refactoring it's much easier to roll your own vision of how
browserify should be using the underlying new libraries as a starting place.

coming soon

Since all the modules in the bundling pipeline are just streams, in a future
release user code will be able to hook into this pipeline with custom
through streams to add
pre-processing, file watching, and other crazy ideas.

--watch was removed and I would like to see userspace modules fill this niche
with the stream hooks.

v2 is all about making browserify more like node itself: a small but
independently useful core with fancy extras pushed off into separate modules so
that core can focus on doing one thing really well.