thlorenz / browserify-shim

📩 Makes CommonJS incompatible files browserifyable.
MIT License
934 stars 87 forks source link

browserify-shim build status

Make CommonJS-Incompatible Files Browserifyable

package.json

{
  "main": "./js/entry.js",
  "browser": {
    "jquery": "./js/vendor/jquery.js"
  },
  "browserify-shim": {
    "jquery": "$",
    "three": "global:THREE"
  },
  "browserify": {
    "transform": [ "browserify-shim" ]
  },
  "dependencies": {
    "browserify-shim": "~3.2.0"
  }
}
browserify . -d -o bundle.js

Table of Contents generated with DocToc

Installation

npm install browserify browserify-shim

For a version compatible with browserify@1.x run npm install browserify-shim@1.x instead.

For a version compatible with the v2 API npm install browserify-shim@2.x instead.

Features

The core features of browserify-shim are:

Additionally, it handles the following real-world edge cases:

Since browserify-shim is a proper browserify transform you can publish packages with files that need to be shimmed, granted that you specify the shim config inside the package.json.

When browserify resolves your package it will run the browserify-shim transform and thus shim what's necessary when generating the bundle.

browserify-shim walks upwards from each source file and uses the first "browserify-shim" configuration it finds in a package.json file. You can't shim files outside your project from your project's package. You can add multiple package.json files as long as browserify-shim can always find a package above each source file with the right configuration.

API

You Will Always

1. Install browserify-shim dependency

In most cases you want to install it as a devDependency via:

npm install -D browserify-shim

2. Register browserify-shim as a transform with browserify

Inside package.json add:

{ 
  "browserify": {
    "transform": [ "browserify-shim" ]
  }
}

Browserify transforms are run in order and may modify your source code along the way. You'll typically want to include browserify-shim last.

3. Provide browserify-shim config

Inside package.json add:

{
  "browserify-shim": {
    "./js/vendor/jquery.js": "$",
    "three": "global:THREE"
  }
}

The above includes ./js/vendor/jquery.js (relative to the package.json) in the bundle and exports window.$.

Additionally it exposes window.THREE as three, so you can var three = require('three'). More info below.

Short Form vs. Long Form config

Since jquery does not depend on other shimmed modules and thus has no depends field, we used the short form to specify its exports, however the example above is equivalent to:

{
  "browserify-shim": {
    "./js/vendor/jquery.js": { "exports": "$" }
  }
}

You will sometimes

a) Expose global variables via global:*

In some cases the libraries you are using are very large and you'd prefer to add them via a script tag instead to get the following benefits:

We'll show how this works by taking the rather huge yet awesome THREE.js library as an example:

1. add script tag for library you want to expose
<!-- index.html -->
<head>
  <meta charset=utf-8 />
  <script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/three.js/r61/three.min.js"></script>
</head>
2. Add expose global config to package.json
{
  "browserify-shim": {
    "three": "global:THREE"
  }
}
2.a. Add expose global config to external shim config

In case you are using an external shim config, you may achieve the same by specifying the global via an exports.

module.exports = {
  'three': { exports: 'global:THREE' }
}

more about external configs here

Note: THREE.js attaches window.THREE.

3. Require library by the name it was exposed as
var THREE = require('three');
Why not just var THREE = window.THREE?

You want to avoid spreading the knowledge that THREE is a global and stay consistent in how you resolve dependencies. Additionally if THREE would ever be published to npm and you decide to install it from there, you don't have to change any of your code since it already is requireing it properly.

b) Use aliases

You may expose files under a different name via the browser field and refer to them under that alias in the shim config:

{
  "browser": {
    "jquery": "./js/vendor/jquery.js"
  },
  "browserify-shim": {
    "jquery": "$"
  }
}

This also allows you to require this module under the alias, i.e.: var $ = require('jquery').

c) Provide an external shim config

{
  "browserify-shim": "./config/shim.js"
}

The external shim format is very similar to the way in which the shim is specified inside the package.json. See below for more details.

d) Diagnose what browserify-shim is doing

You may encounter problems when your shim config isn't properly setup. In that case you can diagnose them via the BROWSERIFYSHIM_DIAGNOSTICS flag.

Simply set the flag when building your bundle, i.e.:

BROWSERIFYSHIM_DIAGNOSTICS=1 browserify -d . -o js/bundle.js

or in a build.js script add: process.env.BROWSERIFYSHIM_DIAGNOSTICS=1 to the top.

Multi Shim Example including dependencies

Some libraries depend on other libraries to have attached their exports to the window for historical reasons :(. (Hopefully soon we can truly say that this bad design is history.)

In this contrived example we are shimming four libraries since none of them are commonJS compatible:

We will be using the depends field in order to ensure that a dependency is included and initialized before a library that depends on it is initialized.

Below are three examples, each showing a way to properly shim the above mentioned modules.

a) Config inside package.json without aliases

{
  "browserify": {
    "transform": [ "browserify-shim" ]
  },
  "browserify-shim": {
    "./vendor/x.js"    :  "$",
    "./vendor/x-ui.js" :  { "depends": [ "./vendor/x.js" ] },
    "./vendor/y.js"    :  { "exports": "Y", "depends": [ "./vendor/x.js:$" ] },
    "./vendor/z.js"    :  { "exports": "zorro", "depends": [ "./vendor/x.js:$", "./vendor/y.js:YNOT" ] }
  }
}

Note: the depends array consists of entries of the format path-to-file:export

b) Config inside package.json with aliases

{
  "browserify": {
    "transform": [ "browserify-shim" ]
  },
  "browser": {
    "x"    :  "./vendor/x.js",
    "x-ui" :  "./vendor/x-ui.js",
    "y"    :  "./vendor/y.js",
    "z"    :  "./vendor/z.js"
  },
   "browserify-shim": {
    "x"    :  "$",
    "x-ui" :  { "depends": [ "x" ] },
    "y"    :  { "exports": "Y", "depends": [ "x:$" ] },
    "z"    :  { "exports": "zorro", "depends": [ "x:$", "y:YNOT" ] }
  }
}

Note: the depends entries make use of the aliases as well alias:export

c) Config inside ./config/shim.js without aliases

package.json

{
  "browserify": {
    "transform": [ "browserify-shim" ]
  },
  "browserify-shim": "./config/shim.js"
}

shim.js

module.exports = {
  '../vendor/x.js'    :  { 'exports': '$' },
  '../vendor/x-ui.js' :  { 'depends': { '../vendor/x.js': null } },
  '../vendor/y.js'    :  { 'exports': 'Y', 'depends': { '../vendor/x.js': '$' } },
  '../vendor/z.js'    :  { 'exports': 'zorro', 'depends': { '../vendor/x.js': '$', '../vendor/y.js': 'YNOT' } }
}

Note: all paths are relative to ./config/shim.js instead of the package.json.

The main difference to a) is the depends field specification. Instead it being an array of strings it expresses its dependencies as a hashmap:

More Examples