diff options
author | Myles Borins <myles.borins@gmail.com> | 2020-04-26 09:47:31 +0300 |
---|---|---|
committer | Myles Borins <mylesborins@google.com> | 2020-05-05 20:48:26 +0300 |
commit | 1ffd182264dcf02e010aae3dc88406c2db9efcfb (patch) | |
tree | cdf133277f7c33c31fdeb88e9257899cd4b0fba3 /doc/api/esm.md | |
parent | 26f150022f9b4d4709c1d4ad450c6f9e9fce8d0e (diff) |
doc: explicitly doc package.exports is breaking
If package authors don't explicitly include all previously supported
entry points introducing package.exports will be a Semver-Major change.
Add a warning about this behavior and offer two potential solutions
for module authors.
Refs: https://github.com/then/is-promise/issues/20
PR-URL: https://github.com/nodejs/node/pull/33074
Reviewed-By: Guy Bedford <guybedford@gmail.com>
Reviewed-By: Jan Krems <jan.krems@gmail.com>
Reviewed-By: Geoffrey Booth <webmaster@geoffreybooth.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Diffstat (limited to 'doc/api/esm.md')
-rw-r--r-- | doc/api/esm.md | 81 |
1 files changed, 66 insertions, 15 deletions
diff --git a/doc/api/esm.md b/doc/api/esm.md index 5047600f988..1c1317b6df7 100644 --- a/doc/api/esm.md +++ b/doc/api/esm.md @@ -183,25 +183,75 @@ versions of Node.js, but its capabilities are limited: it only defines the main entry point of the package. The `"exports"` field provides an alternative to `"main"` where the package -main entry point can be defined while also encapsulating the package, preventing -any other entry points besides those defined in `"exports"`. If package entry -points are defined in both `"main"` and `"exports"`, the latter takes precedence -in versions of Node.js that support `"exports"`. [Conditional Exports][] can -also be used within `"exports"` to define different package entry points per -environment, including whether the package is referenced via `require` or via -`import`. +main entry point can be defined while also encapsulating the package, +**preventing any other entry points besides those defined in `"exports"`**. +This encapsulation allows module authors to define a public interface for +their package. If both `"exports"` and `"main"` are defined, the `"exports"` field takes -precedence over `"main"`. +precedence over `"main"`. `"exports"` are not specific to ES modules or +CommonJS; `"main"` will be overridden by `"exports"` if it exists. As such +`"main"` cannot be used as a fallback for CommonJS but it can be used as a +fallback for legacy versions of Node.js that do not support the `"exports"` +field. + +[Conditional Exports][] can be used within `"exports"` to define different +package entry points per environment, including whether the package is +referenced via `require` or via `import`. For more information about supporting +both CommonJS and ES Modules in a single package please consult +[the dual CommonJS/ES module packages section][]. + +**Warning**: Introducing the `"exports"` field prevents consumers of a package +from using any entry points that are not defined, including the `package.json` +(e.g. `require('your-package/package.json')`. **This will likely be a breaking +change.** + +To make the introduction of `"exports"` non-breaking, ensure that every +previously supported entry point is exported. It is best to explicitly specify +entry points so that the package’s public API is well-defined. For example, +a project that previous exported `main`, `lib`, +`feature`, and the `package.json` could use the following `package.exports`: -Both `"main"` and `"exports"` entry points are not specific to ES modules or -CommonJS; `"main"` will be overridden by `"exports"` in a `require` so it is -not a CommonJS fallback. +```json +{ + "name": "my-mod", + "exports": { + ".": "./lib/index.js", + "./lib": "./lib/index.js", + "./lib/index": "./lib/index.js", + "./lib/index.js": "./lib/index.js", + "./feature": "./feature/index.js", + "./feature/index.js": "./feature/index.js", + "./package.json": "./package.json" + } +} +``` + +Alternatively a project could choose to export entire folders: + +```json +{ + "name": "my-mod", + "exports": { + ".": "./lib/index.js", + "./lib": "./lib/index.js", + "./lib/": "./lib/", + "./feature": "./feature/index.js", + "./feature/": "./feature/", + "./package.json": "./package.json" + } +} +``` -This is important with regard to `require`, since `require` of ES module files -throws an error in all versions of Node.js. To create a package that works both -in modern Node.js via `import` and `require` and also legacy Node.js versions, -see [the dual CommonJS/ES module packages section][]. +As a last resort, package encapsulation can be disabled entirely by creating an +export for the root of the package `"./": "./"`. This will expose every file in +the package at the cost of disabling the encapsulation and potential tooling +benefits this provides. As the ES Module loader in Node.js enforces the use of +[the full specifier path][], exporting the root rather than being explicit +about entry is less expressive than either of the prior examples. Not only +will encapsulation be lost but module consumers will be unable to +`import feature from 'my-mod/feature'` as they will need to provide the full +path `import feature from 'my-mod/feature/index.js`. #### Main Entry Point Export @@ -1746,6 +1796,7 @@ success! [dynamic instantiate hook]: #esm_code_dynamicinstantiate_code_hook [import an ES or CommonJS module for its side effects only]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#Import_a_module_for_its_side_effects_only [special scheme]: https://url.spec.whatwg.org/#special-scheme +[the full specifier path]: #esm_mandatory_file_extensions [the official standard format]: https://tc39.github.io/ecma262/#sec-modules [the dual CommonJS/ES module packages section]: #esm_dual_commonjs_es_module_packages [transpiler loader example]: #esm_transpiler_loader |