diff --git a/doc/api/esm.md b/doc/api/esm.md index 5949758d26ca5e..3efd3c5298eb66 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 @@ -1750,6 +1800,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