public
Last active

browser field spec for package.json

  • Download Gist
browser.md
Markdown

The browser field is provided by a module author as a hint to javascript bundlers or component tools when preparing modules for client side use.

terms

Below are common terms used in the rest of the document

server

This is a non-dom based javascript execution environment. It usually only contains the base javascript language spec libraries and objects along with modules to communicate with OS features (available through commonjs require).

client

This is a browser execution environment. It may provide additional built in objects exposed in the global namespace. It is a specialized execution environment which provides builtin capabilities beyond the base javascript language spec.

commonjs

A require system for specifying which modules or files a particular file uses.

bundler

A tool which takes a plain javascript package and creates client usable files. It may include, but is not limited to: replacing modules or files with client versions (since the client may already provide the functionality), merging all the dependencies into a single file, etc.

package.json

Metadata information about a module.

packaging

The use of a bundler to create a file(s) suitable for running on a client.

overview

When a javascript module is prepared for use on a client there are two major concerns: certain features are already provided by the client, and certain features are not available. Features provided by a client can include http requests, websockets, dom manipulation. Features not available would include tcp sockets, system disk IO.

The browser field is where the module author can hint to the bundler which elements (other modules or source files) need to be replaced when packaging.

spec

alternate main - basic

When you specify a single string for the browser field, it will replace main and be the module entry point.

"browser": "./browser/specific/main.js"

Whenever another module requires your module by name, the bundler will load javascript from ./browser/specific/main.js instead of the typical main field entry point (or index.js by default).

replace specific files - advanced

In many cases, there is a large amount of code which is applicable to both client and server. If is easier to just replace some files instead of craeting a completely new entry point. To do this, just specify an object versus a single string.

When using an object. The left hand side (key) is the name of a module or file you wish to replace and the right side is the replacement.

"browser": {
    "module-a": "./shims/module-a.js",
    "./server/only.js": "./shims/server-only.js"
}

Now when you package your code, uses of module-a will be replaced with code from ./shims/module-a.js versus the module code itself and anytime ./server/only.js is used, it will be replaced with ./shims/server-only.js

If a module you depend on already includes a browser field, then you don't have to do anything special. Whenever you require that module, the bundler SHOULD use the hint provided by the module. The ws module is an example of this behavior.

ignore a module

You can simply prevent a module or file from being loaded into a bundle by specifying a value of false for any of the keys. This is useful if you know certain codepaths will not be executed client side but find it awkward to split up or change the code structure.

"browser": {
    "module-a": false,
    "./server/only.js": "./shims/server-only.js"
}

The above will cause the following to return an empty object into a.

var a = require('module-a');

Note: The use of false should be restructed to only the most essential places in your app code where other browser field approaches do not work. It is discouraged but sometimes a necessary approach.

advantages

Using the browser field in package.json allows a module author to clearly articulate which files are innapropriate for client use and provide alternatives. It allows the module code (and subsequently dependants on the module) to not use preprocessor hacks, source code changes, or runtime detection hacks to identify which code is appropriate when creating a client bound package.

notes

  • If your module is pure javascript and can run in both client and server environments, then you do not need a browser field.
  • The browser field is located in the package.json file as it provides metadata in the form of a hint to bundlers about what files you have indicated are targeted for the client. It allows your source code to remain clean and free of hacks.
  • Consider that the client environment as the special case as it exposes objects into the global space to provide certain features and limits others.

Nice! I'd love to see this spec implemented in all bundlers :smile:

Added to "Complementary Resources" section of "Front-End Package Manager Comparison":
http://git.io/_ZWfVA#complementary-resources

How would a module author provide the following:

  • themod.js (authored directly, CJS style)
  • themod-amd.js (generated at publish time)

Given a package.json that had a browser field, should the module author even attempt to provide both files? Should the author specify transforms, and rely on the consumer using a tool the author specified a transform for? It would be nice if the consumer could use the same identifier in both scenarios.

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.