aboutsummaryrefslogtreecommitdiff
path: root/doc/api
diff options
context:
space:
mode:
authorGuy Bedford <guybedford@gmail.com>2019-08-02 01:30:32 -0400
committerGuy Bedford <guybedford@gmail.com>2019-08-12 06:24:28 -0400
commit2103ae483547831281e1e3882029e37d445689a7 (patch)
tree78199f21a7898ea63ef1e64b85c8f74c6b669171 /doc/api
parent15b2d1331082c66adf608bb4de9987aa47a25358 (diff)
downloadandroid-node-v8-2103ae483547831281e1e3882029e37d445689a7.tar.gz
android-node-v8-2103ae483547831281e1e3882029e37d445689a7.tar.bz2
android-node-v8-2103ae483547831281e1e3882029e37d445689a7.zip
module: pkg exports validations and fallbacks
PR-URL: https://github.com/nodejs/node/pull/28949 Reviewed-By: Bradley Farias <bradley.meck@gmail.com> Reviewed-By: Jan Krems <jan.krems@gmail.com>
Diffstat (limited to 'doc/api')
-rw-r--r--doc/api/esm.md69
-rw-r--r--doc/api/modules.md19
2 files changed, 65 insertions, 23 deletions
diff --git a/doc/api/esm.md b/doc/api/esm.md
index 79da763e8f..17d98ab208 100644
--- a/doc/api/esm.md
+++ b/doc/api/esm.md
@@ -242,13 +242,13 @@ throw when an attempt is made to import them:
```js
import submodule from 'es-module-package/private-module.js';
-// Throws - Package exports error
+// Throws - Module not found
```
> Note: this is not a strong encapsulation as any private modules can still be
> loaded by absolute paths.
-Folders can also be mapped with package exports as well:
+Folders can also be mapped with package exports:
<!-- eslint-skip -->
```js
@@ -268,8 +268,24 @@ import feature from 'es-module-package/features/x.js';
If a package has no exports, setting `"exports": false` can be used instead of
`"exports": {}` to indicate the package does not intend for submodules to be
exposed.
-This is just a convention that works because `false`, just like `{}`, has no
-iterable own properties.
+
+Any invalid exports entries will be ignored. This includes exports not
+starting with `"./"` or a missing trailing `"/"` for directory exports.
+
+Array fallback support is provided for exports, similarly to import maps
+in order to be forward-compatible with fallback workflows in future:
+
+<!-- eslint-skip -->
+```js
+{
+ "exports": {
+ "./submodule": ["not:valid", "./submodule.js"]
+ }
+}
+```
+
+Since `"not:valid"` is not a supported target, `"./submodule.js"` is used
+instead as the fallback, as if it were the only target.
## <code>import</code> Specifiers
@@ -660,7 +676,7 @@ CommonJS loader. Additional formats such as _"addon"_ can be extended in future
updates.
In the following algorithms, all subroutine errors are propagated as errors
-of these top-level routines.
+of these top-level routines unless stated otherwise.
_isMain_ is **true** when resolving the Node.js application entry point.
@@ -681,6 +697,9 @@ _isMain_ is **true** when resolving the Node.js application entry point.
> 1. Note: _specifier_ is now a bare specifier.
> 1. Set _resolvedURL_ the result of
> **PACKAGE_RESOLVE**(_specifier_, _parentURL_).
+> 1. If _resolvedURL_ contains any percent encodings of _"/"_ or _"\\"_ (_"%2f"_
+> and _"%5C"_ respectively), then
+> 1. Throw an _Invalid Specifier_ error.
> 1. If the file at _resolvedURL_ does not exist, then
> 1. Throw a _Module Not Found_ error.
> 1. Set _resolvedURL_ to the real path of _resolvedURL_.
@@ -737,7 +756,7 @@ _isMain_ is **true** when resolving the Node.js application entry point.
> 1. If _pjson_ is **null**, then
> 1. Throw a _Module Not Found_ error.
> 1. If _pjson.main_ is a String, then
-> 1. Let _resolvedMain_ be the concatenation of _packageURL_, "/", and
+> 1. Let _resolvedMain_ be the URL resolution of _packageURL_, "/", and
> _pjson.main_.
> 1. If the file at _resolvedMain_ exists, then
> 1. Return _resolvedMain_.
@@ -746,8 +765,6 @@ _isMain_ is **true** when resolving the Node.js application entry point.
> 1. Let _legacyMainURL_ be the result applying the legacy
> **LOAD_AS_DIRECTORY** CommonJS resolver to _packageURL_, throwing a
> _Module Not Found_ error for no resolution.
-> 1. If _legacyMainURL_ does not end in _".js"_ then,
-> 1. Throw an _Unsupported File Extension_ error.
> 1. Return _legacyMainURL_.
**PACKAGE_EXPORTS_RESOLVE**(_packageURL_, _packagePath_, _exports_)
@@ -755,19 +772,42 @@ _isMain_ is **true** when resolving the Node.js application entry point.
> 1. Set _packagePath_ to _"./"_ concatenated with _packagePath_.
> 1. If _packagePath_ is a key of _exports_, then
> 1. Let _target_ be the value of _exports[packagePath]_.
-> 1. If _target_ is not a String, continue the loop.
-> 1. Return the URL resolution of the concatenation of _packageURL_ and
-> _target_.
+> 1. Return **PACKAGE_EXPORTS_TARGET_RESOLVE**(_packageURL_, _target_,
+> _""_).
> 1. Let _directoryKeys_ be the list of keys of _exports_ ending in
> _"/"_, sorted by length descending.
> 1. For each key _directory_ in _directoryKeys_, do
> 1. If _packagePath_ starts with _directory_, then
> 1. Let _target_ be the value of _exports[directory]_.
-> 1. If _target_ is not a String, continue the loop.
> 1. Let _subpath_ be the substring of _target_ starting at the index
> of the length of _directory_.
-> 1. Return the URL resolution of the concatenation of _packageURL_,
-> _target_ and _subpath_.
+> 1. Return **PACKAGE_EXPORTS_TARGET_RESOLVE**(_packageURL_, _target_,
+> _subpath_).
+> 1. Throw a _Module Not Found_ error.
+
+**PACKAGE_EXPORTS_TARGET_RESOLVE**(_packageURL_, _target_, _subpath_)
+> 1. If _target_ is a String, then
+> 1. If _target_ does not start with _"./"_, throw a _Module Not Found_
+> error.
+> 1. If _subpath_ has non-zero length and _target_ does not end with _"/"_,
+> throw a _Module Not Found_ error.
+> 1. If _target_ or _subpath_ contain any _"node_modules"_ segments including
+> _"node_modules"_ percent-encoding, throw a _Module Not Found_ error.
+> 1. Let _resolvedTarget_ be the URL resolution of the concatenation of
+> _packageURL_ and _target_.
+> 1. If _resolvedTarget_ is contained in _packageURL_, then
+> 1. Let _resolved_ be the URL resolution of the concatenation of
+> _subpath_ and _resolvedTarget_.
+> 1. If _resolved_ is contained in _resolvedTarget_, then
+> 1. Return _resolved_.
+> 1. Otherwise, if _target_ is an Array, then
+> 1. For each item _targetValue_ in _target_, do
+> 1. If _targetValue_ is not a String, continue the loop.
+> 1. Let _resolved_ be the result of
+> **PACKAGE_EXPORTS_TARGET_RESOLVE**(_packageURL_, _targetValue_,
+> _subpath_), continuing the loop on abrupt completion.
+> 1. Assert: _resolved_ is a String.
+> 1. Return _resolved_.
> 1. Throw a _Module Not Found_ error.
**ESM_FORMAT**(_url_, _isMain_)
@@ -790,6 +830,7 @@ _isMain_ is **true** when resolving the Node.js application entry point.
**READ_PACKAGE_SCOPE**(_url_)
> 1. Let _scopeURL_ be _url_.
> 1. While _scopeURL_ is not the file system root,
+> 1. If _scopeURL_ ends in a _"node_modules"_ path segment, return **null**.
> 1. Let _pjson_ be the result of **READ_PACKAGE_JSON**(_scopeURL_).
> 1. If _pjson_ is not **null**, then
> 1. Return _pjson_.
diff --git a/doc/api/modules.md b/doc/api/modules.md
index bf8209965e..7197ef6ae2 100644
--- a/doc/api/modules.md
+++ b/doc/api/modules.md
@@ -202,11 +202,12 @@ NODE_MODULES_PATHS(START)
5. return DIRS
```
-If `--experimental-exports` is enabled,
-node allows packages loaded via `LOAD_NODE_MODULES` to explicitly declare
-which filepaths to expose and how they should be interpreted.
-This expands on the control packages already had using the `main` field.
-With this feature enabled, the `LOAD_NODE_MODULES` changes as follows:
+If `--experimental-exports` is enabled, Node.js allows packages loaded via
+`LOAD_NODE_MODULES` to explicitly declare which file paths to expose and how
+they should be interpreted. This expands on the control packages already had
+using the `main` field.
+
+With this feature enabled, the `LOAD_NODE_MODULES` changes are:
```txt
LOAD_NODE_MODULES(X, START)
@@ -224,10 +225,10 @@ RESOLVE_BARE_SPECIFIER(DIR, X)
b. If "exports" is null or undefined, GOTO 3.
c. Find the longest key in "exports" that the subpath starts with.
d. If no such key can be found, throw "not found".
- e. If the key matches the subpath entirely, return DIR/name/${exports[key]}.
- f. If either the key or exports[key] do not end with a slash (`/`),
- throw "not found".
- g. Return DIR/name/${exports[key]}${subpath.slice(key.length)}.
+ e. let RESOLVED_URL =
+ PACKAGE_EXPORTS_TARGET_RESOLVE(pathToFileURL(DIR/name), exports[key],
+ subpath.slice(key.length)), as defined in the esm resolver.
+ f. return fileURLToPath(RESOLVED_URL)
3. return DIR/X
```