Skip to content

pkg.module

Rich Harris edited this page May 23, 2017 · 5 revisions

Right now, most libraries are distributed as one or more of the following: AMD, CommonJS or browser globals. Because of that, developers use tools that work with those legacy formats - such as Browserify and RequireJS - which creates further incentive for library authors to use them.

What we want is for everyone to use ES2015 modules! (Here are a few reasons why they're better.)

pkg.module is how we get there.

(Note: Tools such as rollup-plugin-node-resolve and Webpack 2 treat jsnext:main and module interchangeably. They're the same thing, except that module is more likely to become standardised.)

pkg.module will point to a module that has ES2015 module syntax but otherwise only syntax features that the target environments support.

Typically, a library will be accompanied with a package.json file (this is mandatory if you're publishing on npm, for example). That file will often specify a pkg.main property - something like this:

{
  "name": "my-package",
  "version": "0.1.0",
  "main": "dist/my-package.js"
}

That instructs Browserify or Webpack or [insert module bundler here] to include the contents of dist/my-package.js – plus any dependencies it has – in your bundle when you call require('my-package') in your app or library.

But for ES2015-aware tools like Rollup, using the CommonJS (or Universal Module Definition) build isn't ideal, because we can't then take advantage of ES2015 module features. So assuming that you've written your package as ES2015 modules, you can generate an ES2015 module build alongside your CommonJS/UMD build:

{
  "name": "my-package",
  "version": "0.1.0",
  "main": "dist/my-package.umd.js",
  "module": "dist/my-package.esm.js"
}

Now we're cooking with gas - my-package continues to work for everyone using legacy module formats, but people using ES2015 module bundlers such as Rollup don't have to compromise. Everyone wins!

Wait, it just means import and export – not other future JavaScript features?

Yes. If you're using things like arrow functions and classes (or more exotic features like decorators and object spread), you should transpile them if you want your library to work in environments that don't support those features. Otherwise, whoever uses your library will have to transpile it themselves, including configuring their transpiler to handle your code, which might involve esoteric transforms and conflicting versions of Babel.

It's a frequent source of frustration. Be a responsible library author and ship code that actually runs in the environments you support!

What about Michael Jackson script?

Node is probably going to use the .mjs file extension as a way to distinguish between ES2015 modules and CommonJS ones. If and when that happens, it will be easy to generate .mjs files instead if you have an existing pkg.module workflow — our advice is to not worry about it yet.

We need your help!

The dark days of JavaScript dependency management are coming to an end, but it won't happen by itself.

If you're writing a package, strongly consider using pkg.module. And lobby politely (or better yet, submit a pull request to) the developers of your favourite libraries to do the same thing. Once we have a critical mass, we can abandon the legacy formats once and for all, and move on to solving more interesting problems.