@@ -183,25 +183,75 @@ versions of Node.js, but its capabilities are limited: it only defines the main |
183 | 183 | entry point of the package. |
184 | 184 |
|
185 | 185 | The `"exports"` field provides an alternative to `"main"` where the package |
186 | | -main entry point can be defined while also encapsulating the package, preventing |
187 | | -any other entry points besides those defined in `"exports"`. If package entry |
188 | | -points are defined in both `"main"` and `"exports"`, the latter takes precedence |
189 | | -in versions of Node.js that support `"exports"`. [Conditional Exports][] can |
190 | | -also be used within `"exports"` to define different package entry points per |
191 | | -environment, including whether the package is referenced via `require` or via |
192 | | -`import`. |
| 186 | +main entry point can be defined while also encapsulating the package, |
| 187 | +**preventing any other entry points besides those defined in `"exports"`**. |
| 188 | +This encapsulation allows module authors to define a public interface for |
| 189 | +their package. |
193 | 190 |
|
194 | 191 | If both `"exports"` and `"main"` are defined, the `"exports"` field takes |
195 | | -precedence over `"main"`. |
| 192 | +precedence over `"main"`. `"exports"` are not specific to ES modules or |
| 193 | +CommonJS; `"main"` will be overridden by `"exports"` if it exists. As such |
| 194 | +`"main"` cannot be used as a fallback for CommonJS but it can be used as a |
| 195 | +fallback for legacy versions of Node.js that do not support the `"exports"` |
| 196 | +field. |
| 197 | + |
| 198 | +[Conditional Exports][] can be used within `"exports"` to define different |
| 199 | +package entry points per environment, including whether the package is |
| 200 | +referenced via `require` or via `import`. For more information about supporting |
| 201 | +both CommonJS and ES Modules in a single package please consult |
| 202 | +[the dual CommonJS/ES module packages section][]. |
| 203 | + |
| 204 | +**Warning**: Introducing the `"exports"` field prevents consumers of a package |
| 205 | +from using any entry points that are not defined, including the `package.json` |
| 206 | +(e.g. `require('your-package/package.json')`. **This will likely be a breaking |
| 207 | +change.** |
| 208 | + |
| 209 | +To make the introduction of `"exports"` non-breaking, ensure that every |
| 210 | +previously supported entry point is exported. It is best to explicitly specify |
| 211 | +entry points so that the package’s public API is well-defined. For example, |
| 212 | +a project that previous exported `main`, `lib`, |
| 213 | +`feature`, and the `package.json` could use the following `package.exports`: |
196 | 214 |
|
197 | | -Both `"main"` and `"exports"` entry points are not specific to ES modules or |
198 | | -CommonJS; `"main"` will be overridden by `"exports"` in a `require` so it is |
199 | | -not a CommonJS fallback. |
| 215 | +```json |
| 216 | +{ |
| 217 | + "name": "my-mod", |
| 218 | + "exports": { |
| 219 | + ".": "./lib/index.js", |
| 220 | + "./lib": "./lib/index.js", |
| 221 | + "./lib/index": "./lib/index.js", |
| 222 | + "./lib/index.js": "./lib/index.js", |
| 223 | + "./feature": "./feature/index.js", |
| 224 | + "./feature/index.js": "./feature/index.js", |
| 225 | + "./package.json": "./package.json" |
| 226 | + } |
| 227 | +} |
| 228 | +``` |
| 229 | + |
| 230 | +Alternatively a project could choose to export entire folders: |
| 231 | + |
| 232 | +```json |
| 233 | +{ |
| 234 | + "name": "my-mod", |
| 235 | + "exports": { |
| 236 | + ".": "./lib/index.js", |
| 237 | + "./lib": "./lib/index.js", |
| 238 | + "./lib/": "./lib/", |
| 239 | + "./feature": "./feature/index.js", |
| 240 | + "./feature/": "./feature/", |
| 241 | + "./package.json": "./package.json" |
| 242 | + } |
| 243 | +} |
| 244 | +``` |
200 | 245 |
|
201 | | -This is important with regard to `require`, since `require` of ES module files |
202 | | -throws an error in all versions of Node.js. To create a package that works both |
203 | | -in modern Node.js via `import` and `require` and also legacy Node.js versions, |
204 | | -see [the dual CommonJS/ES module packages section][]. |
| 246 | +As a last resort, package encapsulation can be disabled entirely by creating an |
| 247 | +export for the root of the package `"./": "./"`. This will expose every file in |
| 248 | +the package at the cost of disabling the encapsulation and potential tooling |
| 249 | +benefits this provides. As the ES Module loader in Node.js enforces the use of |
| 250 | +[the full specifier path][], exporting the root rather than being explicit |
| 251 | +about entry is less expressive than either of the prior examples. Not only |
| 252 | +will encapsulation be lost but module consumers will be unable to |
| 253 | +`import feature from 'my-mod/feature'` as they will need to provide the full |
| 254 | +path `import feature from 'my-mod/feature/index.js`. |
205 | 255 |
|
206 | 256 | #### Main Entry Point Export |
207 | 257 |
|
@@ -1746,6 +1796,7 @@ success! |
1746 | 1796 | [dynamic instantiate hook]: #esm_code_dynamicinstantiate_code_hook |
1747 | 1797 | [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 |
1748 | 1798 | [special scheme]: https://url.spec.whatwg.org/#special-scheme |
| 1799 | +[the full specifier path]: #esm_mandatory_file_extensions |
1749 | 1800 | [the official standard format]: https://tc39.github.io/ecma262/#sec-modules |
1750 | 1801 | [the dual CommonJS/ES module packages section]: #esm_dual_commonjs_es_module_packages |
1751 | 1802 | [transpiler loader example]: #esm_transpiler_loader |
|