Last active

Embed URL

HTTPS clone URL

SSH clone URL

You can clone with HTTPS or SSH.

Download Gist

browser field spec for package.json

View browser.md

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 creating 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/client-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/client-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.

It is not completely related to this, but i want to extend the concept of client side (only) libraries npm. Imo the problem of client side dependencies with js, css, etc. is still not solved yet. There are tools for some parts of it but no common specification or configuration etc. What about other client side assets like css, less or images? At the moment thats completely missing in my opinion.

There is already the directories property in the CommonJS spec, but it is missing keys for static assets like css, less, images, etc. (http://wiki.commonjs.org/wiki/Packages/1.0#Optional_Fields and https://www.npmjs.org/doc/files/package.json.html#directories)

There are tons of great client side libraries on npm but every single one is different to use / consume in the frontend when it comes to css, less, sass, images, fonts, etc. How to include them? What files should be included? In which order? It would be great if package maintainers could clearly describe what additional assets, besides javascript are shipped with the npm package and how to consume them.

Lets take bootstrap as an example (original https://github.com/twbs/bootstrap/blob/master/package.json)
Why not?


{
  "name": "bootstrap",
  "description": "The most popular front-end framework for developing responsive, mobile first projects on the web.",
  "version": "3.3.0",
  "homepage": "http://getbootstrap.com",
  "author": "Twitter, Inc.",
  "scripts": {
    "test": "grunt test"
  },
  "directories": {
    "assets": {
       "css": ["dist/css/bootstrap.min.css","dist/css/bootstrap-theme.min.css"],
       "js":["dist/js/bootstrap.min.js"]
       "font":["dist/fonts/glyphicons-halflings-regular.eot","dist/fonts/glyphicons-halflings-regular.eot","dist/fonts/glyphicons-halflings-regular.svg","dist/fonts/glyphicons-halflings-regular.ttf", "dist/fonts/glyphicons-halflings-regular.woff"]
    }
  },
  "main": "./dist/js/npm",
  "repository": {
    "type": "git",
    "url": "https://github.com/twbs/bootstrap.git"
  },
  "bugs": {
    "url": "https://github.com/twbs/bootstrap/issues"
  },
  "license": {
    "type": "MIT",
    "url": "https://github.com/twbs/bootstrap/blob/master/LICENSE"
  },

  "jspm": {
    "main": "js/bootstrap",
    "directories": {
      "example": "examples",
      "lib": "dist"
    },
    "shim": {
      "js/bootstrap": {
        "imports": "jquery",
        "exports": "$"
      }
    },
    "buildConfig": {
      "uglify": true
    }
  }
}

This could be even further extended to configure which assets are already "compiled", minified or packaged for usage or which assets are optional (bootstrap-theme.min.css).

Here are some very popular packages with js, css and/or other assets on npm without any further configuration:

Package Managers like bower, component, npm or other tools like browserify, etc. could use this information only or in addition to their separate configuration files.

What do you think?
cheers

It would be great if we could expand this spec with subarg syntax:
https://github.com/substack/node-browserify/issues/1046

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.