Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

doc: esm: options for package authors #29497

Closed
wants to merge 4 commits into from
Closed
Changes from 1 commit
Commits
File filter...
Filter file types
Jump to…
Jump to file or symbol
Failed to load files and symbols.

Always

Just for now

doc: esm: expand discussion of publishing cjs/esm packages

  • Loading branch information...
GeoffreyBooth committed Sep 3, 2019
commit c28ee9be88a46737f9d4b8df834459e3f3287bf4
@@ -97,14 +97,11 @@ if the nearest parent `package.json` contains `"type": "module"`.
import './startup.js'; // Loaded as ES module because of package.json
```

It is recommended that package authors always include the `"type"` field, even
in packages where it is unnecessary because all sources are CommonJS (and so
therefore the default `"type": "commonjs"` is implied) or because there are no
files ending in `.js` (for example, all files end in `.mjs` or `.cjs` or
`.wasm`, etc.). Being explicit about the `type` of the package will future-proof
the package in case Node.js’ default type ever changes, and it will also make
things easier for build tools and loaders to determine how the files in the
package should be interpreted.
Package authors should include the `"type"` field, even in packages where all
sources are CommonJS. Being explicit about the `type` of the package will
future-proof the package in case the default type of Node.js ever changes, and
it will also make things easier for build tools and loaders to determine how the
files in the package should be interpreted.

### Package Scope and File Extensions

@@ -220,13 +217,48 @@ be interpreted as CommonJS.

The `"main"` field can point to exactly one file, regardless of whether the
package is referenced via `require` (in a CommonJS context) or `import` (in an
ES module context). Package authors who want to publish a package to be used in
both contexts can do so by setting `"main"` to point to the CommonJS entry point
and informing the package’s users of the path to the ES module entry point. Such
a package would be accessible like `require('pkg')` and `import
'pkg/module.mjs'`. Alternatively the package `"main"` could point to the ES
module entry point and legacy users could be informed of the CommonJS entry
point path, e.g. `require('pkg/commonjs')`.
ES module context).

#### Compatibility with CommonJS-Only Versions of Node.js

Prior to the introduction of support for ES modules in Node.js, it was a common
pattern for package authors to include both CommonJS and ES module JavaScript
sources in their package, with `package.json` `"main"` specifying the CommonJS
entry point and `package.json` `"module"` specifying the ES module entry point.
This conversation was marked as resolved by GeoffreyBooth

This comment has been minimized.

Copy link
@ljharb

ljharb Sep 11, 2019

Member
Suggested change
entry point and `package.json` `"module"` specifying the ES module entry point.
entry point and `package.json` `"module"` or `"jsnext:main"` specifying the ES module entry point.

This comment has been minimized.

Copy link
@GeoffreyBooth

GeoffreyBooth Sep 11, 2019

Author Contributor

I don’t think this is quite accurate. jsnext:main doesn’t necessarily imply that the source is ESM, only that it uses syntax that probably needs transpilation. It could be CommonJS.

This comment has been minimized.

Copy link
@ljharb

ljharb Sep 11, 2019

Member

ok, fair enough

This enabled Node.js to run the CommonJS entry point while build tools such as
bundlers used the ES module entry point, since Node.js ignored (and still
ignores) `"module"`.
This conversation was marked as resolved by GeoffreyBooth

This comment has been minimized.

Copy link
@ljharb

ljharb Sep 11, 2019

Member
Suggested change
ignores) `"module"`.
ignores) both `"module"` and `"jsnext:main"`.

Node.js can now run ES module entry points, but it remains impossible for a
package to define separate CommonJS and ES module entry points. This is for good
reason: the `pkg` variable created from `import pkg from 'pkg'` is not the same
singleton as the `pkg` variable created from `const pkg = require('pkg')`, so if
both are referenced within the same app (including dependencies), unexpected
behavior might occur.

There are two general approaches to addressing this limitation while still
publishing a package that contains both CommonJS and ES module sources:

1. Document a new ES module entry point that’s not the package `"main"`, e.g.
`import pkg from 'pkg/module.mjs'` (or `import 'pkg/esm'`, if using [package
exports][]). The package `"main"` would still point to a CommonJS file, and
thus the package would remain compatible with older versions of Node.js that
lack support for ES modules.

1. Switch the package `"main"` entry point to an ES module file as part of a
semver major version bump. This version and above would only be usable on ES
module-supporting versions of Node.js. If the package still contains a
CommonJS version, it would be accessible via a path within the package, e.g.
`require('pkg/commonjs')`; this is essentially the inverse of the previous
approach. Package consumers who are using CommonJS-only versions of Node.js
would need to update their code from `require('pkg')` to e.g.
`require('pkg/commonjs')`.

Of course, a package could also include _only_ CommonJS or ES module sources. An
This conversation was marked as resolved by GeoffreyBooth

This comment has been minimized.

Copy link
@Trott

Trott Sep 16, 2019

Member

Micro nit: I try to avoid unnecessary typography for emphasis (since we inevitably end up with a mixture of italics, bold, and even all-caps). So totally-optional suggestion:

Suggested change
Of course, a package could also include _only_ CommonJS or ES module sources. An
Of course, a package could also include only CommonJS or ES module sources. An
existing package could make a semver major bump to an ES module-only version,
This conversation was marked as resolved by GeoffreyBooth

This comment has been minimized.

Copy link
@Trott

Trott Sep 16, 2019

Member

Totally optional, could happen at a later date or not at all: It's possible that the average reader will understand the term "semver major bump" but if there's wording that might be less jargon-y, let's use it. (Maybe spell out "Semantic Version" and/or provide a link to an appropriate semver.org URL?)

This comment has been minimized.

Copy link
@Trott

Trott Sep 16, 2019

Member

(Or maybe just the word bump might be clarified?)

This comment has been minimized.

Copy link
@GeoffreyBooth

GeoffreyBooth Sep 16, 2019

Author Contributor

I tried rephrasing these two in a bit clearer way, please let me know what you think of the new language.

that would only be supported in ES module-supporting versions of Node.js (and
other runtimes). New packages could be published containing only ES module
sources, and would be compatible only with ES module-supporting runtimes.

### Package Exports

@@ -908,6 +940,7 @@ success!
[CommonJS]: modules.html
[ECMAScript-modules implementation]: https://github.com/nodejs/modules/blob/master/doc/plan-for-new-modules-implementation.md
[package exports]: #esm_package_exports
This conversation was marked as resolved by GeoffreyBooth

This comment has been minimized.

Copy link
@Trott

Trott Sep 16, 2019

Member

We try to keep these bottom references in ASCII order. One day, we'll get a lint rule for that. In the meantime, can you move this one to between dynamic instantiate hook and special scheme below?

[ES Module Integration Proposal for Web Assembly]: https://github.com/webassembly/esm-integration
[Node.js EP for ES Modules]: https://github.com/nodejs/node-eps/blob/master/002-es-modules.md
[Terminology]: #esm_terminology
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.