@@ -260,6 +260,9 @@ that would only be supported in ES module-supporting versions of Node.js (and
260
260
other runtimes). New packages could be published containing only ES module
261
261
sources, and would be compatible only with ES module-supporting runtimes.
262
262
263
+ To define separate package entry points for use by ` require ` and by ` import ` ,
264
+ see [ Conditional Exports] [ ] .
265
+
263
266
### Package Exports
264
267
265
268
By 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
313
316
` "exports": {} ` to indicate the package does not intend for submodules to be
314
317
exposed.
315
318
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:
317
324
318
325
<!-- eslint-skip -->
319
326
``` js
320
- // ./node_modules/es-module-package/package.json
321
327
{
322
328
" exports" : {
323
- " ." : " ./main .js"
329
+ " ./submodule " : [ " not:valid " , " ./submodule .js" ]
324
330
}
325
331
}
326
332
```
327
333
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 ` .
331
339
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:
334
417
335
418
<!-- eslint-skip -->
336
419
``` js
337
- // ./node_modules/es-module-package/package.json
338
420
{
339
421
" exports" : " ./main.js"
340
422
}
341
423
```
342
424
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.
345
428
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:
348
442
349
443
<!-- eslint-skip -->
350
444
``` js
351
445
{
352
446
" exports" : {
353
- " ./submodule" : [" not:valid" , " ./submodule.js" ]
447
+ " require" : " ./main.cjs" ,
448
+ " default" : " ./main.js"
354
449
}
355
450
}
356
451
```
357
452
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
+ ```
360
467
361
468
## <code >import</code > Specifiers
362
469
@@ -806,6 +913,9 @@ of these top-level routines unless stated otherwise.
806
913
807
914
_isMain_ is **true** when resolving the Node.js application entry point.
808
915
916
+ _defaultEnv_ is the conditional environment name priority array,
917
+ ` [" node" , " default" ]` .
918
+
809
919
<details>
810
920
<summary>Resolver algorithm specification</summary>
811
921
@@ -905,14 +1015,16 @@ _isMain_ is **true** when resolving the Node.js application entry point.
905
1015
> 1. If _pjson_ is **null**, then
906
1016
> 1. Throw a _Module Not Found_ error.
907
1017
> 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_.
909
1026
> 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_, _""_).
916
1028
> 1. If _pjson.main_ is a String, then
917
1029
> 1. Let _resolvedMain_ be the URL resolution of _packageURL_, "/", and
918
1030
> _pjson.main_.
@@ -926,13 +1038,14 @@ _isMain_ is **true** when resolving the Node.js application entry point.
926
1038
> 1. Return _legacyMainURL_.
927
1039
928
1040
**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
931
1044
> 1. Set _packagePath_ to _"./"_ concatenated with _packagePath_.
932
1045
> 1. If _packagePath_ is a key of _exports_, then
933
1046
> 1. Let _target_ be the value of _exports\[ packagePath\] _.
934
1047
> 1. Return **PACKAGE_EXPORTS_TARGET_RESOLVE**(_packageURL_, _target_,
935
- > _""_).
1048
+ > _""_, _defaultEnv_ ).
936
1049
> 1. Let _directoryKeys_ be the list of keys of _exports_ ending in
937
1050
> _"/"_, sorted by length descending.
938
1051
> 1. For each key _directory_ in _directoryKeys_, do
@@ -941,10 +1054,10 @@ _isMain_ is **true** when resolving the Node.js application entry point.
941
1054
> 1. Let _subpath_ be the substring of _target_ starting at the index
942
1055
> of the length of _directory_.
943
1056
> 1. Return **PACKAGE_EXPORTS_TARGET_RESOLVE**(_packageURL_, _target_,
944
- > _subpath_).
1057
+ > _subpath_, _defaultEnv_ ).
945
1058
> 1. Throw a _Module Not Found_ error.
946
1059
947
- **PACKAGE_EXPORTS_TARGET_RESOLVE**(_packageURL_, _target_, _subpath_)
1060
+ **PACKAGE_EXPORTS_TARGET_RESOLVE**(_packageURL_, _target_, _subpath_, _env_ )
948
1061
949
1062
> 1. If _target_ is a String, then
950
1063
> 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.
960
1073
> _subpath_ and _resolvedTarget_.
961
1074
> 1. If _resolved_ is contained in _resolvedTarget_, then
962
1075
> 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_.
963
1084
> 1. Otherwise, if _target_ is an Array, then
964
1085
> 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.
966
1087
> 1. Let _resolved_ be the result of
967
1088
> **PACKAGE_EXPORTS_TARGET_RESOLVE**(_packageURL_, _targetValue_,
968
- > _subpath_), continuing the loop on abrupt completion.
1089
+ > _subpath_, _env_ ), continuing the loop on abrupt completion.
969
1090
> 1. Assert: _resolved_ is a String.
970
1091
> 1. Return _resolved_.
971
1092
> 1. Throw a _Module Not Found_ error.
@@ -1033,6 +1154,7 @@ success!
1033
1154
` ` `
1034
1155
1035
1156
[CommonJS]: modules.html
1157
+ [Conditional Exports]: #esm_conditional_exports
1036
1158
[ECMAScript-modules implementation]: https://github.com/nodejs/modules/blob/master/doc/plan-for-new-modules-implementation.md
1037
1159
[ES Module Integration Proposal for Web Assembly]: https://github.com/webassembly/esm-integration
1038
1160
[Node.js EP for ES Modules]: https://github.com/nodejs/node-eps/blob/master/002-es-modules.md
@@ -1045,7 +1167,7 @@ success!
1045
1167
[` import ` ]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
1046
1168
[` module .createRequire ()` ]: modules.html#modules_module_createrequire_filename
1047
1169
[` module .syncBuiltinESMExports ()` ]: modules.html#modules_module_syncbuiltinesmexports
1048
- [dynamic instantiate hook]: #esm_dynamic_instantiate_hook
1049
1170
[package exports]: #esm_package_exports
1171
+ [dynamic instantiate hook]: #esm_dynamic_instantiate_hook
1050
1172
[special scheme]: https://url.spec.whatwg.org/#special-scheme
1051
1173
[the official standard format]: https://tc39.github.io/ecma262/#sec-modules
0 commit comments