| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -161,6 +161,15 @@ the ability to import a directory that has an index file. | |||
| 161 | 161 | ||
| 162 | 162 | Please see [customizing esm specifier resolution][] for example usage. | |
| 163 | 163 | ||
| 164 | + ### `--experimental-conditional-exports | ||
| 165 | + <!-- YAML | ||
| 166 | + added: REPLACEME | ||
| 167 | + --> | ||
| 168 | + | ||
| 169 | + Enable experimental support for the `"require"` and `"node"` conditional | ||
| 170 | + package export resolutions. | ||
| 171 | + See [Conditional Exports][] for more information. | ||
| 172 | + | ||
| 164 | 173 | ### `--experimental-json-modules` | |
| 165 | 174 | <!-- YAML | |
| 166 | 175 | added: v12.9.0 | |
@@ -1055,6 +1064,7 @@ Node.js options that are allowed are: | |||
| 1055 | 1064 | * `--enable-fips` | |
| 1056 | 1065 | * `--enable-source-maps` | |
| 1057 | 1066 | * `--es-module-specifier-resolution` | |
| 1067 | + * `--experimental-conditional-exports` | ||
| 1058 | 1068 | * `--experimental-json-modules` | |
| 1059 | 1069 | * `--experimental-loader` | |
| 1060 | 1070 | * `--experimental-modules` | |
@@ -1359,3 +1369,4 @@ greater than `4` (its current default value). For more information, see the | |||
| 1359 | 1369 | [libuv threadpool documentation]: http://docs.libuv.org/en/latest/threadpool.html | |
| 1360 | 1370 | [remote code execution]: https://www.owasp.org/index.php/Code_Injection | |
| 1361 | 1371 | [context-aware]: addons.html#addons_context_aware_addons | |
| 1372 | + [Conditional Exports]: esm.html#esm_conditional_exports | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -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 | ## `import` Specifiers | |
| 362 | 469 | ||
@@ -805,6 +912,9 @@ of these top-level routines unless stated otherwise. | |||
| 805 | 912 | ||
| 806 | 913 | _isMain_ is **true** when resolving the Node.js application entry point. | |
| 807 | 914 | ||
| 915 | + _defaultEnv_ is the conditional environment name priority array, | ||
| 916 | + `["node", "default"]`. | ||
| 917 | + | ||
| 808 | 918 | <details> | |
| 809 | 919 | <summary>Resolver algorithm specification</summary> | |
| 810 | 920 | ||
@@ -904,14 +1014,16 @@ _isMain_ is **true** when resolving the Node.js application entry point. | |||
| 904 | 1014 | > 1. If _pjson_ is **null**, then | |
| 905 | 1015 | > 1. Throw a _Module Not Found_ error. | |
| 906 | 1016 | > 1. If _pjson.exports_ is not **null** or **undefined**, then | |
| 907 | - > 1. If _pjson.exports_ is a String or Array, then | ||
| 1017 | + > 1. If _exports_ is an Object with both a key starting with _"."_ and a key | ||
| 1018 | + > not starting with _"."_, throw a "Invalid Package Configuration" error. | ||
| 1019 | + > 1. If _pjson.exports_ is a String or Array, or an Object containing no | ||
| 1020 | + > keys starting with _"."_, then | ||
| 1021 | + > 1. Return **PACKAGE_EXPORTS_TARGET_RESOLVE**(_packageURL_, | ||
| 1022 | + > _pjson.exports_, _""_). | ||
| 1023 | + > 1. If _pjson.exports_ is an Object containing a _"."_ property, then | ||
| 1024 | + > 1. Let _mainExport_ be the _"."_ property in _pjson.exports_. | ||
| 908 | 1025 | > 1. Return **PACKAGE_EXPORTS_TARGET_RESOLVE**(_packageURL_, | |
| 909 | - > _pjson.exports_, "")_. | ||
| 910 | - > 1. If _pjson.exports is an Object, then | ||
| 911 | - > 1. If _pjson.exports_ contains a _"."_ property, then | ||
| 912 | - > 1. Let _mainExport_ be the _"."_ property in _pjson.exports_. | ||
| 913 | - > 1. Return **PACKAGE_EXPORTS_TARGET_RESOLVE**(_packageURL_, | ||
| 914 | - > _mainExport_, "")_. | ||
| 1026 | + > _mainExport_, _""_). | ||
| 915 | 1027 | > 1. If _pjson.main_ is a String, then | |
| 916 | 1028 | > 1. Let _resolvedMain_ be the URL resolution of _packageURL_, "/", and | |
| 917 | 1029 | > _pjson.main_. | |
@@ -925,13 +1037,14 @@ _isMain_ is **true** when resolving the Node.js application entry point. | |||
| 925 | 1037 | > 1. Return _legacyMainURL_. | |
| 926 | 1038 | ||
| 927 | 1039 | **PACKAGE_EXPORTS_RESOLVE**(_packageURL_, _packagePath_, _exports_) | |
| 928 | - | ||
| 929 | - > 1. If _exports_ is an Object, then | ||
| 1040 | + > 1. If _exports_ is an Object with both a key starting with _"."_ and a key not | ||
| 1041 | + > starting with _"."_, throw an "Invalid Package Configuration" error. | ||
| 1042 | + > 1. If _exports_ is an Object and all keys of _exports_ start with _"."_, then | ||
| 930 | 1043 | > 1. Set _packagePath_ to _"./"_ concatenated with _packagePath_. | |
| 931 | 1044 | > 1. If _packagePath_ is a key of _exports_, then | |
| 932 | 1045 | > 1. Let _target_ be the value of _exports\[packagePath\]_. | |
| 933 | 1046 | > 1. Return **PACKAGE_EXPORTS_TARGET_RESOLVE**(_packageURL_, _target_, | |
| 934 | - > _""_). | ||
| 1047 | + > _""_, _defaultEnv_). | ||
| 935 | 1048 | > 1. Let _directoryKeys_ be the list of keys of _exports_ ending in | |
| 936 | 1049 | > _"/"_, sorted by length descending. | |
| 937 | 1050 | > 1. For each key _directory_ in _directoryKeys_, do | |
@@ -940,10 +1053,10 @@ _isMain_ is **true** when resolving the Node.js application entry point. | |||
| 940 | 1053 | > 1. Let _subpath_ be the substring of _target_ starting at the index | |
| 941 | 1054 | > of the length of _directory_. | |
| 942 | 1055 | > 1. Return **PACKAGE_EXPORTS_TARGET_RESOLVE**(_packageURL_, _target_, | |
| 943 | - > _subpath_). | ||
| 1056 | + > _subpath_, _defaultEnv_). | ||
| 944 | 1057 | > 1. Throw a _Module Not Found_ error. | |
| 945 | 1058 | ||
| 946 | - **PACKAGE_EXPORTS_TARGET_RESOLVE**(_packageURL_, _target_, _subpath_) | ||
| 1059 | + **PACKAGE_EXPORTS_TARGET_RESOLVE**(_packageURL_, _target_, _subpath_, _env_) | ||
| 947 | 1060 | ||
| 948 | 1061 | > 1. If _target_ is a String, then | |
| 949 | 1062 | > 1. If _target_ does not start with _"./"_, throw a _Module Not Found_ | |
@@ -959,12 +1072,20 @@ _isMain_ is **true** when resolving the Node.js application entry point. | |||
| 959 | 1072 | > _subpath_ and _resolvedTarget_. | |
| 960 | 1073 | > 1. If _resolved_ is contained in _resolvedTarget_, then | |
| 961 | 1074 | > 1. Return _resolved_. | |
| 1075 | + > 1. Otherwise, if _target_ is a non-null Object, then | ||
| 1076 | + > 1. If _target_ has an object key matching one of the names in _env_, then | ||
| 1077 | + > 1. Let _targetValue_ be the corresponding value of the first object key | ||
| 1078 | + > of _target_ in _env_. | ||
| 1079 | + > 1. Let _resolved_ be the result of **PACKAGE_EXPORTS_TARGET_RESOLVE** | ||
| 1080 | + > (_packageURL_, _targetValue_, _subpath_, _env_). | ||
| 1081 | + > 1. Assert: _resolved_ is a String. | ||
| 1082 | + > 1. Return _resolved_. | ||
| 962 | 1083 | > 1. Otherwise, if _target_ is an Array, then | |
| 963 | 1084 | > 1. For each item _targetValue_ in _target_, do | |
| 964 | - > 1. If _targetValue_ is not a String, continue the loop. | ||
| 1085 | + > 1. If _targetValue_ is an Array, continue the loop. | ||
| 965 | 1086 | > 1. Let _resolved_ be the result of | |
| 966 | 1087 | > **PACKAGE_EXPORTS_TARGET_RESOLVE**(_packageURL_, _targetValue_, | |
| 967 | - > _subpath_), continuing the loop on abrupt completion. | ||
| 1088 | + > _subpath_, _env_), continuing the loop on abrupt completion. | ||
| 968 | 1089 | > 1. Assert: _resolved_ is a String. | |
| 969 | 1090 | > 1. Return _resolved_. | |
| 970 | 1091 | > 1. Throw a _Module Not Found_ error. | |
@@ -1032,6 +1153,7 @@ success! | |||
| 1032 | 1153 | ``` | |
| 1033 | 1154 | ||
| 1034 | 1155 | [CommonJS]: modules.html | |
| 1156 | + [Conditional Exports]: #esm_conditional_exports | ||
| 1035 | 1157 | [ECMAScript-modules implementation]: https://github.com/nodejs/modules/blob/master/doc/plan-for-new-modules-implementation.md | |
| 1036 | 1158 | [ES Module Integration Proposal for Web Assembly]: https://github.com/webassembly/esm-integration | |
| 1037 | 1159 | [Node.js EP for ES Modules]: https://github.com/nodejs/node-eps/blob/master/002-es-modules.md | |
@@ -1044,7 +1166,7 @@ success! | |||
| 1044 | 1166 | [`import`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import | |
| 1045 | 1167 | [`module.createRequire()`]: modules.html#modules_module_createrequire_filename | |
| 1046 | 1168 | [`module.syncBuiltinESMExports()`]: modules.html#modules_module_syncbuiltinesmexports | |
| 1047 | - [dynamic instantiate hook]: #esm_dynamic_instantiate_hook | ||
| 1048 | 1169 | [package exports]: #esm_package_exports | |
| 1170 | + [dynamic instantiate hook]: #esm_dynamic_instantiate_hook | ||
| 1049 | 1171 | [special scheme]: https://url.spec.whatwg.org/#special-scheme | |
| 1050 | 1172 | [the official standard format]: https://tc39.github.io/ecma262/#sec-modules | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -232,12 +232,17 @@ RESOLVE_BARE_SPECIFIER(DIR, X) | |||
| 232 | 232 | 2. If X matches this pattern and DIR/name/package.json is a file: | |
| 233 | 233 | a. Parse DIR/name/package.json, and look for "exports" field. | |
| 234 | 234 | b. If "exports" is null or undefined, GOTO 3. | |
| 235 | - c. Find the longest key in "exports" that the subpath starts with. | ||
| 236 | - d. If no such key can be found, throw "not found". | ||
| 237 | - e. let RESOLVED_URL = | ||
| 235 | + c. If "exports" is an object with some keys starting with "." and some keys | ||
| 236 | + not starting with ".", throw "invalid config". | ||
| 237 | + c. If "exports" is a string, or object with no keys starting with ".", treat | ||
| 238 | + it as having that value as its "." object property. | ||
| 239 | + d. If subpath is "." and "exports" does not have a "." entry, GOTO 3. | ||
| 240 | + e. Find the longest key in "exports" that the subpath starts with. | ||
| 241 | + f. If no such key can be found, throw "not found". | ||
| 242 | + g. let RESOLVED_URL = | ||
| 238 | 243 | PACKAGE_EXPORTS_TARGET_RESOLVE(pathToFileURL(DIR/name), exports[key], | |
| 239 | 244 | subpath.slice(key.length)), as defined in the esm resolver. | |
| 240 | - f. return fileURLToPath(RESOLVED_URL) | ||
| 245 | + h. return fileURLToPath(RESOLVED_URL) | ||
| 241 | 246 | 3. return DIR/X | |
| 242 | 247 | ``` | |
| 243 | 248 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -108,6 +108,9 @@ Requires Node.js to be built with | |||
| 108 | 108 | .It Fl -es-module-specifier-resolution | |
| 109 | 109 | Select extension resolution algorithm for ES Modules; either 'explicit' (default) or 'node' | |
| 110 | 110 | . | |
| 111 | + .It Fl -experimental-conditional-exports | ||
| 112 | + Enable experimental support for "require" and "node" conditional export targets. | ||
| 113 | + . | ||
| 111 | 114 | .It Fl -experimental-json-modules | |
| 112 | 115 | Enable experimental JSON interop support for the ES Module loader. | |
| 113 | 116 | . | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -976,7 +976,7 @@ E('ERR_INVALID_OPT_VALUE', (name, value) => | |||
| 976 | 976 | E('ERR_INVALID_OPT_VALUE_ENCODING', | |
| 977 | 977 | 'The value "%s" is invalid for option "encoding"', TypeError); | |
| 978 | 978 | E('ERR_INVALID_PACKAGE_CONFIG', | |
| 979 | - 'Invalid package config in \'%s\' imported from %s', Error); | ||
| 979 | + 'Invalid package config for \'%s\', %s', Error); | ||
| 980 | 980 | E('ERR_INVALID_PERFORMANCE_MARK', | |
| 981 | 981 | 'The "%s" performance mark has not been set', Error); | |
| 982 | 982 | E('ERR_INVALID_PROTOCOL', | |
| Back | FazBrowse Home | New Git URL |
0 commit comments