Home

Awesome

browserify-middleware

<img src="http://i.imgur.com/6cyfaYS.png" align="right" />

middleware for browserify v2 with sensible defaults for the ultimate in ease of use

In addition to the basics, browserify-middleware has the following features out of the box:

With the exception of serving up directories (which requires req.path from express) everything is entirely framework independent. Simply pass in req res, and a callback that will only be called in the event of an error.

If you think I've missed something, be sure to open an issue or submit a pull request.

Build Status Coverage Status Dependency Status NPM version

<a target='_blank' rel='nofollow' href='https://app.codesponsor.io/link/gg9sZwctSLxyov1sJwW6pfyS/ForbesLindesay/browserify-middleware'> <img alt='Sponsor' width='888' height='68' src='https://app.codesponsor.io/embed/gg9sZwctSLxyov1sJwW6pfyS/ForbesLindesay/browserify-middleware.svg' /> </a>

Usage

See example directory for a complete server

var browserify = require('browserify-middleware');
var express = require('express');
var app = express();

//provide browserified versions of all the files in a directory
app.use('/js', browserify(__dirname + '/client/dir'));

//provide a browserified file at a path
app.get('/js/file.js', browserify(__dirname + '/client/file.js'));

//provide a bundle exposing `require` for a few npm packages.
app.get('/js/bundle.js', browserify(['hyperquest', 'concat-stream']));

//provide a bundle for a few npm packages plus run main.js
app.get('/js/bundle.js', browserify(['hyperquest', 'concat-stream', {__dirname + '/client/main.js': {run: true}}]));

app.listen(3000);

Multiple Bundles Example

Multiple bundles can sometimes lead to better caching performance. If you had multiple different JavaScript modules in ./client that all depended on hyperquest and concat-stream and were used on different pages, you may want to split those two modules into separate files so that they are only loaded once for someone browsing arround the site:

var shared = ['hyperquest', 'concat-stream'];
app.get('/js/bundle.js', browserify(shared));
app.use('/js', browserify('./client', {external: shared}))

Then on your HTML pages you can just have:

page1.html

<script src="/js/bundle.js"></script>
<script src="/js/beep.js"></script>

page2.html

<script src="/js/bundle.js"></script>
<script src="/js/boop.js"></script>

This way, booth beep.js and boop.js can require the shared modules (hyperquest and concat-stream) but they aren't actually contained within that file.

API

browserify('./path/to/file.js'[, options])

Return the middleware to serve a browserified version of the file. The file path is relative to the calling module, not to process.cwd().

browserify('./path/to/directory/'[, options])

Return the middleware to serve a browserified version of all the files in a directory. The directory path is relative to the calling module, not to process.cwd().

browserify(['module-a', 'module-b'][, options])

Return middleware that will expose require for each of the modules in the array. This will work even if those modules are also in the external array.

browserify([{'module-d': {expose: 'dee'}}][, options])

Require module-d with custom options (to be passed on to browserify). In this case module-d will be exposed as dee. This can be mixed and matched with plain strings. Note that these modules must not appear in the external array.

options / settings

The options passed to each middleware function override the defaults specified in settings.

Setings has two properties settings.production and settings.development which specify the default settings for each environment. The current environment is specified by settings.mode and defaults to process.env.NODE_ENV || 'development'

Production defaults:

production.cache = true; // equivalent to "public, max-age=60"
production.precompile = true;
production.minify = true;
production.gzip = true;
production.debug = false;

To update:

browserify.settings.production('cache', '7 days');

Development defaults:

development.cache = 'dynamic';
development.precompile = false;
development.minify = false;
development.gzip = false;
development.debug = true;

To update:

browserify.settings.development('gzip', true);

The following defaults are the same for production and development:

external = [];
ignore = [];
ignoreMissing = false;
transform = [];
insertGlobals = false;
detectGlobals = true;
standalone = false;
grep = /\.js$/

To update:

browserify.settings('external', ['hyperquest']);
//or
browserify.settings({
  ignoreMissing: true,
  insertGlobals: true,
  transform: ['rfileify']
});

Custom Environments:

You can also create a new custom environment:

var test = browserify.settings.env('test');
test('minify', true);
//or
test({
  debug: true
});

cache

The cache setting determines how long content can be cached in the client's web browsers (and any caching proxies) and whether or not to cache bundles server side. Any value other than false will result in them being cached server side. The 'dynamic' cache option is special. It works like watchify and only re-compiles the files that have changed. This is the fastest option for development. It does not enable any client side caching.

If cache is true the client will recieve Cache Control of "public, max-age=60", which caches for 60 seconds.

If cache is a string in the form accepted by ms it becomes: "public, max-age=" + (ms(cache)/1000)

If cache is a number, it is treated as being in milliseconds so becomes: "public, max-age=" + (cache/1000)

If cache is an object of the form {private: true || false, maxAge: '10 minutes'} it becomes the apropriate string.

If cache is any other string it will be sent directly to the client.

N.B. that if caching is enabled, the server never times out its cache, no matter what the timeout set for the client.

precompile

The precompile setting enables bundles to be precompiled/built and readily cached immediately on server startup. This option is not available when using browserify with a directory. If precompile is set to true, the bundle will be compiled & cached at server start.

// Precompile a browserified file at a path
app.get('/js/file.js', browserify('./client/file.js', {
  cache: true,
  precompile: true
}));

// Precompile a bundle exposing `require` for a few npm packages.
app.get('/js/bundle.js', browserify(['hyperquest', 'concat-stream'], {
  cache: true,
  precompile: true
}));

N.B. It only makes sense to use precompiling when caching is enabled. If caching is disabled, no precompiling will happen.

minify

If minify is true, UglifyJS will be used to minify the resulting code. This is true by default in production. If you set it to an object, the object will be passed to uglify-js as options:

gzip

If gzip is true, GZip will be enabled when clients support it. This increases the memory required for caching by aproximately 50% but the speed boost can be considerable. It is true by default in production.

debug

If debug is true, a source map will be added to the code. This is very useful when debugging. debug is false in production.

basedir

If debug is true you can provide a string pathname for basedir and the paths of your files in the source-map will be displayed relative to that file. This is great for hiding the details of your local file system or tidying up the debugging of a large app.

plugins

An array of objects of the form {plugin: 'name', options: {object}}.

grep

The regular expression, something like /\.(?:js|coffee|ls)$/, that a filename must pass to be served using browserify from a directory.

hooks

There are a number of hooks that you can implement to modify the source at a few stages of processing.

e.g.

app.get('/index.js', browserify('/index.js', {
  preminify: function (source) {
    return angularJsMinifier(source);
  }
}));

The available hooks are currently:

The main use case you might have for this would be adding extra minfication steps that are able to make additional assumptions about your code. These hooks can return either a string or a Promise for a string.

Others

The remaining settings are all passed through to browserify, you should look at the browserify readme if you want to know more:

You can optionally pass a single item instead of an array to any of the options that take an array.

License

MIT

If you find it useful, a donation via gittip would be appreciated.