mirror of
https://github.com/nodejs/node.git
synced 2026-11-09 15:11:11 -05:00
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>
This commit is contained in:
1 parent
26f150022f
commit
1ffd182264
1 file changed
+66
-15
+66
-15
@@ -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.
|
||||
|
||||
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.
|
||||
[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][].
|
||||
|
||||
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][].
|
||||
**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`:
|
||||
|
||||
```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"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
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
|
||||
|
||||
Reference in new issue
Block a user