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: add current recommendation for ESM/CommonJS dual packages #27957

Closed
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 9 additions & 10 deletions doc/api/esm.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,16 +206,15 @@ a full path including extension: `"./index.mjs"`, not `"./index"`.
If the `package.json` `"type"` field is omitted, a `.js` file in `"main"` will
be interpreted as CommonJS.

> Currently a package can define _either_ a CommonJS entry point **or** an ES
> module entry point; there is no way to specify separate entry points for
> CommonJS and ES module usage. This means that a package entry point can be
> included via `require` or via `import` but not both.
>
> Such a limitation makes it difficult for packages to support both new versions
> of Node.js that understand ES modules and older versions of Node.js that
> understand only CommonJS. There is work ongoing to remove this limitation, and
> it will very likely entail changes to the behavior of `"main"` as defined
> here.
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
Copy link

@robpalme robpalme May 31, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The bracketed clarifications seem a bit ambiguous about whether it's the context of the caller or the callee module. I think we're trying to make a statement about the callee module. And it's not true that import implies either the caller or the callee (if we consider dynamic import()) will be in ESM context. I am finding this hard to word accurately and unambiguously. Maybe something like:

The "main" field can point to exactly one file, regardless of whether the package is referenced via require (which will load the file as CommonJS) or import (which allows Node's loading rules to decide whether to load the file as either an ES module or as CommonJS).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the original is more correct. It is about which loader / module graph the module may be part of, not about the interpretation of the file contents. E.g. in a require context, it may be any number of file formats supported via require._extensions and in an import context it may be any content type supported by the import loader system.

The main point here is that it can only point to exactly one file on disk and it may only be instantiated/interpreted at most once: Either as part of the require module graph or as part of the import module graph (with the additional note that the import graph may point to nodes in the require graph).

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')`.

## <code>import</code> Specifiers

Expand Down