@@ -260,6 +260,9 @@ that would only be supported in ES module-supporting versions of Node.js (and
260260other runtimes). New packages could be published containing only ES module
261261sources, and would be compatible only with ES module-supporting runtimes.
262262
263+ To define separate package entry points for use by ` require ` and by ` import ` ,
264+ see [ Conditional Exports] [ ] .
265+
263266### Package Exports
264267
265268By default, all subpaths from a package can be imported (` import 'pkg/x.js' ` ).
@@ -313,50 +316,154 @@ If a package has no exports, setting `"exports": false` can be used instead of
313316` "exports": {} ` to indicate the package does not intend for submodules to be
314317exposed.
315318
316- Exports can also be used to map the main entry point of a package:
319+ Any invalid exports entries will be ignored. This includes exports not
320+ starting with ` "./" ` or a missing trailing ` "/" ` for directory exports.
321+
322+ Array fallback support is provided for exports, similarly to import maps
323+ in order to be forwards-compatible with possible fallback workflows in future:
317324
318325<!-- eslint-skip -->
319326``` js
320- // ./node_modules/es-module-package/package.json
321327{
322328 " exports" : {
323- " ." : " ./main .js"
329+ " ./submodule " : [ " not:valid " , " ./submodule .js" ]
324330 }
325331}
326332```
327333
328- where the "." indicates loading the package without any subpath. Exports will
329- always override any existing ` "main" ` value for both CommonJS and
330- ES module packages.
334+ Since ` "not:valid" ` is not a supported target, ` "./submodule.js" ` is used
335+ instead as the fallback, as if it were the only target.
336+
337+ Defining a ` "." ` export will define the main entry point for the package,
338+ and will always take precedence over the ` "main" ` field in the ` package.json ` .
331339
332- For packages with only a main entry point, an ` "exports" ` value of just
333- a string is also supported:
340+ This allows defining a different entry point for Node.js versions that support
341+ ECMAScript modules and versions that don't, for example:
342+
343+ <!-- eslint-skip -->
344+ ``` js
345+ {
346+ " main" : " ./main-legacy.cjs" ,
347+ " exports" : {
348+ " ." : " ./main-modern.cjs"
349+ }
350+ }
351+ ```
352+
353+ #### Conditional Exports
354+
355+ Conditional exports provide a way to map to different paths depending on
356+ certain conditions. They are supported for both CommonJS and ES module imports.
357+
358+ For example, a package that wants to provide different ES module exports for
359+ Node.js and the browser can be written:
360+
361+ <!-- eslint-skip -->
362+ ``` js
363+ // ./node_modules/pkg/package.json
364+ {
365+ " type" : " module" ,
366+ " main" : " ./index.js" ,
367+ " exports" : {
368+ " ./feature" : {
369+ " browser" : " ./feature-browser.js" ,
370+ " default" : " ./feature-default.js"
371+ }
372+ }
373+ }
374+ ```
375+
376+ When resolving the ` "." ` export, if no matching target is found, the ` "main" `
377+ will be used as the final fallback.
378+
379+ The conditions supported in Node.js are matched in the following order:
380+
381+ 1 . ` "require" ` - matched when the package is loaded via ` require() ` .
382+ _ This is currently only supported behind the
383+ ` --experimental-conditional-exports ` flag._
384+ 2 . ` "node" ` - matched for any Node.js environment. Can be a CommonJS or ES
385+ module file. _ This is currently only supported behind the
386+ ` --experimental-conditional-exports ` flag._
387+ 3 . ` "default" ` - the generic fallback that will always match if no other
388+ more specific condition is matched first. Can be a CommonJS or ES module
389+ file.
390+
391+ Using the ` "require" ` condition it is possible to define a package that will
392+ have a different exported value for CommonJS and ES modules, which can be a
393+ hazard in that it can result in having two separate instances of the same
394+ package in use in an application, which can cause a number of bugs.
395+
396+ Other conditions such as ` "browser" ` , ` "electron" ` , ` "deno" ` , ` "react-native" ` ,
397+ etc. could be defined in other runtimes or tools.
398+
399+ #### Exports Sugar
400+
401+ If the ` "." ` export is the only export, the ` "exports" ` field provides sugar
402+ for this case being the direct ` "exports" ` field value.
403+
404+ If the ` "." ` export has a fallback array or string value, then the ` "exports" `
405+ field can be set to this value directly.
406+
407+ <!-- eslint-skip -->
408+ ``` js
409+ {
410+ " exports" : {
411+ " ." : " ./main.js"
412+ }
413+ }
414+ ```
415+
416+ can be written:
334417
335418<!-- eslint-skip -->
336419``` js
337- // ./node_modules/es-module-package/package.json
338420{
339421 " exports" : " ./main.js"
340422}
341423```
342424
343- Any invalid exports entries will be ignored. This includes exports not
344- starting with ` "./" ` or a missing trailing ` "/" ` for directory exports.
425+ When using conditional exports, the rule is that all keys in the object mapping
426+ must not start with a ` "." ` otherwise they would be indistinguishable from
427+ exports subpaths.
345428
346- Array fallback support is provided for exports, similarly to import maps
347- in order to be forward-compatible with fallback workflows in future:
429+ <!-- eslint-skip -->
430+ ``` js
431+ {
432+ " exports" : {
433+ " ." : {
434+ " require" : " ./main.cjs" ,
435+ " default" : " ./main.js"
436+ }
437+ }
438+ }
439+ ```
440+
441+ can be written:
348442
349443<!-- eslint-skip -->
350444``` js
351445{
352446 " exports" : {
353- " ./submodule" : [" not:valid" , " ./submodule.js" ]
447+ " require" : " ./main.cjs" ,
448+ " default" : " ./main.js"
354449 }
355450}
356451```
357452
358- Since ` "not:valid" ` is not a supported target, ` "./submodule.js" ` is used
359- instead as the fallback, as if it were the only target.
453+ If writing any exports value that mixes up these two forms, an error will be
454+ thrown:
455+
456+ <!-- eslint-skip -->
457+ ``` js
458+ {
459+ // Throws on resolution!
460+ " exports" : {
461+ " ./feature" : " ./lib/feature.js" ,
462+ " require" : " ./main.cjs" ,
463+ " default" : " ./main.js"
464+ }
465+ }
466+ ```
360467
361468## <code >import</code > Specifiers
362469
@@ -806,6 +913,9 @@ of these top-level routines unless stated otherwise.
806913
807914_isMain_ is **true** when resolving the Node.js application entry point.
808915
916+ _defaultEnv_ is the conditional environment name priority array,
917+ ` [" node" , " default" ]` .
918+
809919<details>
810920<summary>Resolver algorithm specification</summary>
811921
@@ -905,14 +1015,16 @@ _isMain_ is **true** when resolving the Node.js application entry point.
9051015> 1. If _pjson_ is **null**, then
9061016> 1. Throw a _Module Not Found_ error.
9071017> 1. If _pjson.exports_ is not **null** or **undefined**, then
908- > 1. If _pjson.exports_ is a String or Array, then
1018+ > 1. If _exports_ is an Object with both a key starting with _"."_ and a key
1019+ > not starting with _"."_, throw a "Invalid Package Configuration" error.
1020+ > 1. If _pjson.exports_ is a String or Array, or an Object containing no
1021+ > keys starting with _"."_, then
1022+ > 1. Return **PACKAGE_EXPORTS_TARGET_RESOLVE**(_packageURL_,
1023+ > _pjson.exports_, _""_).
1024+ > 1. If _pjson.exports_ is an Object containing a _"."_ property, then
1025+ > 1. Let _mainExport_ be the _"."_ property in _pjson.exports_.
9091026> 1. Return **PACKAGE_EXPORTS_TARGET_RESOLVE**(_packageURL_,
910- > _pjson.exports_, "")_.
911- > 1. If _pjson.exports is an Object, then
912- > 1. If _pjson.exports_ contains a _"."_ property, then
913- > 1. Let _mainExport_ be the _"."_ property in _pjson.exports_.
914- > 1. Return **PACKAGE_EXPORTS_TARGET_RESOLVE**(_packageURL_,
915- > _mainExport_, "")_.
1027+ > _mainExport_, _""_).
9161028> 1. If _pjson.main_ is a String, then
9171029> 1. Let _resolvedMain_ be the URL resolution of _packageURL_, "/", and
9181030> _pjson.main_.
@@ -926,13 +1038,14 @@ _isMain_ is **true** when resolving the Node.js application entry point.
9261038> 1. Return _legacyMainURL_.
9271039
9281040**PACKAGE_EXPORTS_RESOLVE**(_packageURL_, _packagePath_, _exports_)
929-
930- > 1. If _exports_ is an Object, then
1041+ > 1. If _exports_ is an Object with both a key starting with _"."_ and a key not
1042+ > starting with _"."_, throw an "Invalid Package Configuration" error.
1043+ > 1. If _exports_ is an Object and all keys of _exports_ start with _"."_, then
9311044> 1. Set _packagePath_ to _"./"_ concatenated with _packagePath_.
9321045> 1. If _packagePath_ is a key of _exports_, then
9331046> 1. Let _target_ be the value of _exports\[ packagePath\] _.
9341047> 1. Return **PACKAGE_EXPORTS_TARGET_RESOLVE**(_packageURL_, _target_,
935- > _""_).
1048+ > _""_, _defaultEnv_ ).
9361049> 1. Let _directoryKeys_ be the list of keys of _exports_ ending in
9371050> _"/"_, sorted by length descending.
9381051> 1. For each key _directory_ in _directoryKeys_, do
@@ -941,10 +1054,10 @@ _isMain_ is **true** when resolving the Node.js application entry point.
9411054> 1. Let _subpath_ be the substring of _target_ starting at the index
9421055> of the length of _directory_.
9431056> 1. Return **PACKAGE_EXPORTS_TARGET_RESOLVE**(_packageURL_, _target_,
944- > _subpath_).
1057+ > _subpath_, _defaultEnv_ ).
9451058> 1. Throw a _Module Not Found_ error.
9461059
947- **PACKAGE_EXPORTS_TARGET_RESOLVE**(_packageURL_, _target_, _subpath_)
1060+ **PACKAGE_EXPORTS_TARGET_RESOLVE**(_packageURL_, _target_, _subpath_, _env_ )
9481061
9491062> 1. If _target_ is a String, then
9501063> 1. If _target_ does not start with _"./"_, throw a _Module Not Found_
@@ -960,12 +1073,20 @@ _isMain_ is **true** when resolving the Node.js application entry point.
9601073> _subpath_ and _resolvedTarget_.
9611074> 1. If _resolved_ is contained in _resolvedTarget_, then
9621075> 1. Return _resolved_.
1076+ > 1. Otherwise, if _target_ is a non-null Object, then
1077+ > 1. If _target_ has an object key matching one of the names in _env_, then
1078+ > 1. Let _targetValue_ be the corresponding value of the first object key
1079+ > of _target_ in _env_.
1080+ > 1. Let _resolved_ be the result of **PACKAGE_EXPORTS_TARGET_RESOLVE**
1081+ > (_packageURL_, _targetValue_, _subpath_, _env_).
1082+ > 1. Assert: _resolved_ is a String.
1083+ > 1. Return _resolved_.
9631084> 1. Otherwise, if _target_ is an Array, then
9641085> 1. For each item _targetValue_ in _target_, do
965- > 1. If _targetValue_ is not a String , continue the loop.
1086+ > 1. If _targetValue_ is an Array , continue the loop.
9661087> 1. Let _resolved_ be the result of
9671088> **PACKAGE_EXPORTS_TARGET_RESOLVE**(_packageURL_, _targetValue_,
968- > _subpath_), continuing the loop on abrupt completion.
1089+ > _subpath_, _env_ ), continuing the loop on abrupt completion.
9691090> 1. Assert: _resolved_ is a String.
9701091> 1. Return _resolved_.
9711092> 1. Throw a _Module Not Found_ error.
@@ -1033,6 +1154,7 @@ success!
10331154` ` `
10341155
10351156[CommonJS]: modules.html
1157+ [Conditional Exports]: #esm_conditional_exports
10361158[ECMAScript-modules implementation]: https://github.com/nodejs/modules/blob/master/doc/plan-for-new-modules-implementation.md
10371159[ES Module Integration Proposal for Web Assembly]: https://github.com/webassembly/esm-integration
10381160[Node.js EP for ES Modules]: https://github.com/nodejs/node-eps/blob/master/002-es-modules.md
@@ -1045,7 +1167,7 @@ success!
10451167[` import ` ]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
10461168[` module .createRequire ()` ]: modules.html#modules_module_createrequire_filename
10471169[` module .syncBuiltinESMExports ()` ]: modules.html#modules_module_syncbuiltinesmexports
1048- [dynamic instantiate hook]: #esm_dynamic_instantiate_hook
10491170[package exports]: #esm_package_exports
1171+ [dynamic instantiate hook]: #esm_dynamic_instantiate_hook
10501172[special scheme]: https://url.spec.whatwg.org/#special-scheme
10511173[the official standard format]: https://tc39.github.io/ecma262/#sec-modules
0 commit comments