diff options
author | Guy Bedford <guybedford@gmail.com> | 2019-08-02 01:30:32 -0400 |
---|---|---|
committer | Guy Bedford <guybedford@gmail.com> | 2019-08-12 06:24:28 -0400 |
commit | 2103ae483547831281e1e3882029e37d445689a7 (patch) | |
tree | 78199f21a7898ea63ef1e64b85c8f74c6b669171 /doc/api | |
parent | 15b2d1331082c66adf608bb4de9987aa47a25358 (diff) | |
download | android-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.md | 69 | ||||
-rw-r--r-- | doc/api/modules.md | 19 |
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 ``` |