module: share CJS/ESM resolver fns, refactoring

PR-URL: https://github.com/nodejs/node/pull/34744
Reviewed-By: Jan Krems <jan.krems@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
Guy Bedford
2020-08-09 16:54:01 -07:00
parent 56b25e7a6f
commit f8976a76bb
8 changed files with 332 additions and 583 deletions

View File

@@ -1631,7 +1631,7 @@ future updates.
In the following algorithms, all subroutine errors are propagated as errors
of these top-level routines unless stated otherwise.
_defaultEnv_ is the conditional environment name priority array,
_defaultConditions_ is the conditional environment name array,
`["node", "import"]`.
The resolver can throw the following errors:
@@ -1651,40 +1651,41 @@ The resolver can throw the following errors:
**ESM_RESOLVE**(_specifier_, _parentURL_)
> 1. Let _resolvedURL_ be **undefined**.
> 1. Let _resolved_ be **undefined**.
> 1. If _specifier_ is a valid URL, then
> 1. Set _resolvedURL_ to the result of parsing and reserializing
> 1. Set _resolved_ to the result of parsing and reserializing
> _specifier_ as a URL.
> 1. Otherwise, if _specifier_ starts with _"/"_, _"./"_ or _"../"_, then
> 1. Set _resolvedURL_ to the URL resolution of _specifier_ relative to
> 1. Set _resolved_ to the URL resolution of _specifier_ relative to
> _parentURL_.
> 1. Otherwise, if _specifier_ starts with _"#"_, then
> 1. Set _resolvedURL_ to the result of
> **PACKAGE_INTERNAL_RESOLVE**(_specifier_, _parentURL_).
> 1. If _resolvedURL_ is **null** or **undefined**, throw a
> _Package Import Not Defined_ error.
> 1. Set _resolved_ to the destructured value of the result of
> **PACKAGE_IMPORTS_RESOLVE**(_specifier_, _parentURL_,
> _defaultConditions_).
> 1. Otherwise,
> 1. Note: _specifier_ is now a bare specifier.
> 1. Set _resolvedURL_ the result of
> 1. Set _resolved_ the result of
> **PACKAGE_RESOLVE**(_specifier_, _parentURL_).
> 1. If _resolvedURL_ contains any percent encodings of _"/"_ or _"\\"_ (_"%2f"_
> 1. If _resolved_ contains any percent encodings of _"/"_ or _"\\"_ (_"%2f"_
> and _"%5C"_ respectively), then
> 1. Throw an _Invalid Module Specifier_ error.
> 1. If the file at _resolvedURL_ is a directory, then
> 1. If the file at _resolved_ is a directory, then
> 1. Throw an _Unsupported Directory Import_ error.
> 1. If the file at _resolvedURL_ does not exist, then
> 1. If the file at _resolved_ does not exist, then
> 1. Throw a _Module Not Found_ error.
> 1. Set _resolvedURL_ to the real path of _resolvedURL_.
> 1. Let _format_ be the result of **ESM_FORMAT**(_resolvedURL_).
> 1. Load _resolvedURL_ as module format, _format_.
> 1. Return _resolvedURL_.
> 1. Set _resolved_ to the real path of _resolved_.
> 1. Let _format_ be the result of **ESM_FORMAT**(_resolved_).
> 1. Load _resolved_ as module format, _format_.
> 1. Return _resolved_.
**PACKAGE_RESOLVE**(_packageSpecifier_, _parentURL_)
> 1. Let _packageName_ be *undefined*.
> 1. Let _packageSubpath_ be *undefined*.
> 1. Let _packageName_ be **undefined**.
> 1. If _packageSpecifier_ is an empty string, then
> 1. Throw an _Invalid Module Specifier_ error.
> 1. If _packageSpecifier_ does not start with _"@"_, then
> 1. Set _packageName_ to the substring of _packageSpecifier_ until the first
> _"/"_ separator or the end of the string.
> 1. Otherwise,
> 1. If _packageSpecifier_ does not contain a _"/"_ separator, then
> 1. Throw an _Invalid Module Specifier_ error.
@@ -1692,18 +1693,12 @@ The resolver can throw the following errors:
> until the second _"/"_ separator or the end of the string.
> 1. If _packageName_ starts with _"."_ or contains _"\\"_ or _"%"_, then
> 1. Throw an _Invalid Module Specifier_ error.
> 1. Let _packageSubpath_ be _undefined_.
> 1. If the length of _packageSpecifier_ is greater than the length of
> _packageName_, then
> 1. Set _packageSubpath_ to _"."_ concatenated with the substring of
> 1. Let _packageSubpath_ be _"."_ concatenated with the substring of
> _packageSpecifier_ from the position at the length of _packageName_.
> 1. If _packageSubpath_ contains any _"."_ or _".."_ segments or percent
> encoded strings for _"/"_ or _"\\"_, then
> 1. Throw an _Invalid Module Specifier_ error.
> 1. Let _selfUrl_ be the result of
> **SELF_REFERENCE_RESOLVE**(_packageName_, _packageSubpath_, _parentURL_).
> 1. If _selfUrl_ isn't empty, return _selfUrl_.
> 1. If _packageSubpath_ is _undefined_ and _packageName_ is a Node.js builtin
> **PACKAGE_SELF_RESOLVE**(_packageName_, _packageSubpath_, _parentURL_).
> 1. If _selfUrl_ is not **undefined**, return _selfUrl_.
> 1. If _packageSubpath_ is _"."_ and _packageName_ is a Node.js builtin
> module, then
> 1. Return the string _"nodejs:"_ concatenated with _packageSpecifier_.
> 1. While _parentURL_ is not the file system root,
@@ -1714,142 +1709,61 @@ The resolver can throw the following errors:
> 1. Set _parentURL_ to the parent URL path of _parentURL_.
> 1. Continue the next loop iteration.
> 1. Let _pjson_ be the result of **READ_PACKAGE_JSON**(_packageURL_).
> 1. If _packageSubpath_ is equal to _"./"_, then
> 1. Return _packageURL_ + _"/"_.
> 1. If _packageSubpath_ is _undefined__, then
> 1. Return the result of **PACKAGE_MAIN_RESOLVE**(_packageURL_,
> _pjson_).
> 1. If _pjson_ is not **null** and _pjson_._exports_ is not **null** or
> **undefined**, then
> 1. Let _exports_ be _pjson.exports_.
> 1. Return the _resolved_ destructured value of the result of
> **PACKAGE_EXPORTS_RESOLVE**(_packageURL_, _packageSubpath_,
> _pjson.exports_, _defaultConditions_).
> 1. Otherwise, if _packageSubpath_ is equal to _"."_, then
> 1. Return the result applying the legacy **LOAD_AS_DIRECTORY**
> CommonJS resolver to _packageURL_, throwing a _Module Not Found_
> error for no resolution.
> 1. Otherwise,
> 1. If _pjson_ is not **null** and _pjson_ has an _"exports"_ key, then
> 1. Let _exports_ be _pjson.exports_.
> 1. If _exports_ is not **null** or **undefined**, then
> 1. Let _resolved_ be the result of **PACKAGE_EXPORTS_RESOLVE**(
> _packageURL_, _packageSubpath_, _pjson.exports_).
> 1. If _resolved_ is **null** or **undefined**, throw a
> _Package Path Not Exported_ error.
> 1. Return _resolved_.
> 1. Return the URL resolution of _packageSubpath_ in _packageURL_.
> 1. Throw a _Module Not Found_ error.
**SELF_REFERENCE_RESOLVE**(_packageName_, _packageSubpath_, _parentURL_)
**PACKAGE_SELF_RESOLVE**(_packageName_, _packageSubpath_, _parentURL_)
> 1. Let _packageURL_ be the result of **READ_PACKAGE_SCOPE**(_parentURL_).
> 1. If _packageURL_ is **null**, then
> 1. Return **undefined**.
> 1. Let _pjson_ be the result of **READ_PACKAGE_JSON**(_packageURL_).
> 1. If _pjson_ does not include an _"exports"_ property, then
> 1. If _pjson_ is **null** or if _pjson_._exports_ is **null** or
> **undefined**, then
> 1. Return **undefined**.
> 1. If _pjson.name_ is equal to _packageName_, then
> 1. If _packageSubpath_ is equal to _"./"_, then
> 1. Return _packageURL_ + _"/"_.
> 1. If _packageSubpath_ is _undefined_, then
> 1. Return the result of **PACKAGE_MAIN_RESOLVE**(_packageURL_, _pjson_).
> 1. Otherwise,
> 1. If _pjson_ is not **null** and _pjson_ has an _"exports"_ key, then
> 1. Let _exports_ be _pjson.exports_.
> 1. If _exports_ is not **null** or **undefined**, then
> 1. Let _resolved_ be the result of **PACKAGE_EXPORTS_RESOLVE**(
> _packageURL_, _subpath_, _pjson.exports_).
> 1. If _resolved_ is **null** or **undefined**, throw a
> _Package Path Not Exported_ error.
> 1. Return _resolved_.
> 1. Return the URL resolution of _subpath_ in _packageURL_.
> 1. Return the _resolved_ destructured value of the result of
> **PACKAGE_EXPORTS_RESOLVE**(_packageURL_, _subpath_, _pjson.exports_,
> _defaultConditions_).
> 1. Otherwise, return **undefined**.
**PACKAGE_MAIN_RESOLVE**(_packageURL_, _pjson_)
**PACKAGE_EXPORTS_RESOLVE**(_packageURL_, _subpath_, _exports_, _conditions_)
> 1. If _pjson_ is **null**, then
> 1. Throw a _Module Not Found_ error.
> 1. If _pjson.exports_ is not **null** or **undefined**, then
> 1. If _exports_ is an Object with both a key starting with _"."_ and a key
> not starting with _"."_, throw an _Invalid Package Configuration_ error.
> 1. If _pjson.exports_ is a String or Array, or an Object containing no
> keys starting with _"."_, then
> 1. Let _resolved_ be the result of **PACKAGE_TARGET_RESOLVE**(
> _packageURL_, _pjson.exports_, _""_, **false**, _defaultEnv_).
> 1. If _resolved_ is **null** or **undefined**, throw a
> _Package Path Not Exported_ error.
> 1. Return _resolved_.
> 1. If _pjson.exports_ is an Object containing a _"."_ property, then
> 1. Let _mainExport_ be the _"."_ property in _pjson.exports_.
> 1. Let _resolved_ be the result of **PACKAGE_TARGET_RESOLVE**(
> _packageURL_, _mainExport_, _""_, **false**, _defaultEnv_).
> 1. If _resolved_ is **null** or **undefined**, throw a
> _Package Path Not Exported_ error.
> 1. Return _resolved_.
> 1. Throw a _Package Path Not Exported_ error.
> 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. Return _legacyMainURL_.
**PACKAGE_EXPORTS_RESOLVE**(_packageURL_, _packagePath_, _exports_)
> 1. If _exports_ is an Object with both a key starting with _"."_ and a key not
> starting with _"."_, throw an _Invalid Package Configuration_ error.
> 1. If _exports_ is an Object and all keys of _exports_ start with _"."_, then
> 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. Return **PACKAGE_TARGET_RESOLVE**(_packageURL_, _target_,
> _""_, **false**, _defaultEnv_).
> 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. Let _subpath_ be the substring of _target_ starting at the index
> of the length of _directory_.
> 1. Return **PACKAGE_TARGET_RESOLVE**(_packageURL_, _target_,
> _subpath_, **false**, _defaultEnv_).
> 1. Return **null**.
**PACKAGE_TARGET_RESOLVE**(_packageURL_, _target_, _subpath_, _internal_, _env_)
> 1. If _target_ is a String, then
> 1. If _target_ contains any _"node_modules"_ segments including
> _"node_modules"_ percent-encoding, throw an _Invalid Package Target_
> error.
> 1. If _subpath_ has non-zero length and _target_ does not end with _"/"_,
> throw an _Invalid Module Specifier_ error.
> 1. If _target_ does not start with _"./"_, then
> 1. If _target_ does not start with _"../"_ or _"/"_ and is not a valid
> URL, then
> 1. If _internal_ is **true**, return **PACKAGE_RESOLVE**(
> _target_ + _subpath_, _packageURL_ + _"/"_)_.
> 1. Otherwise throw an _Invalid Package Target_ error.
> 1. Let _resolvedTarget_ be the URL resolution of the concatenation of
> _packageURL_ and _target_.
> 1. If _resolvedTarget_ is not contained in _packageURL_, throw an
> _Invalid Package Target_ error.
> 1. Let _resolved_ be the URL resolution of the concatenation of
> _subpath_ and _resolvedTarget_.
> 1. If _resolved_ is not contained in _resolvedTarget_, throw an
> _Invalid Module Specifier_ error.
> 1. Return _resolved_.
> 1. Otherwise, if _target_ is a non-null Object, then
> 1. If _exports_ contains any index property keys, as defined in ECMA-262
> [6.1.7 Array Index][], throw an _Invalid Package Configuration_ error.
> 1. For each property _p_ of _target_, in object insertion order as,
> 1. If _p_ equals _"default"_ or _env_ contains an entry for _p_, then
> 1. Let _targetValue_ be the value of the _p_ property in _target_.
> 1. Let _resolved_ be the result of **PACKAGE_TARGET_RESOLVE**(
> _packageURL_, _targetValue_, _subpath_, _internal_, _env_)
> 1. If _resolved_ is equal to **undefined**, continue the loop.
> 1. Return _resolved_.
> 1. Return **undefined**.
> 1. Otherwise, if _target_ is an Array, then
> 1. If _target.length is zero, return **null**.
> 1. For each item _targetValue_ in _target_, do
> 1. If _subpath_ is equal to _"."_, then
> 1. Let _mainExport_ be **undefined**.
> 1. If _exports_ is a String or Array, or an Object containing no keys
> starting with _"."_, then
> 1. Set _mainExport_ to _exports_.
> 1. Otherwise if _exports_ is an Object containing a _"."_ property, then
> 1. Set _mainExport_ to _exports_\[_"."_\].
> 1. If _mainExport_ is not **undefined**, then
> 1. Let _resolved_ be the result of **PACKAGE_TARGET_RESOLVE**(
> _packageURL_, _targetValue_, _subpath_, _internal_, _env_),
> continuing the loop on any _Invalid Package Target_ error.
> 1. If _resolved_ is **undefined**, continue the loop.
> 1. Return _resolved_.
> 1. Return or throw the last fallback resolution **null** return or error.
> 1. Otherwise, if _target_ is _null_, return **null**.
> 1. Otherwise throw an _Invalid Package Target_ error.
> _packageURL_, _mainExport_, _""_, **false**, _conditions_).
> 1. If _resolved_ is not **null** or **undefined**, then
> 1. Return _resolved_.
> 1. Otherwise, if _exports_ is an Object and all keys of _exports_ start with
> _"."_, then
> 1. Let _matchKey_ be the string _"./"_ concatenated with _subpath_.
> 1. Let _resolvedMatch_ be result of **PACKAGE_IMPORTS_EXPORTS_RESOLVE**(
> _matchKey_, _exports_, _packageURL_, **false**, _conditions_).
> 1. If _resolvedMatch_._resolve_ is not **null** or **undefined**, then
> 1. Return _resolvedMatch_.
> 1. Throw a _Package Path Not Exported_ error.
**PACKAGE_INTERNAL_RESOLVE**(_specifier_, _parentURL_)
**PACKAGE_IMPORTS_RESOLVE**(_specifier_, _parentURL_, _conditions_)
> 1. Assert: _specifier_ begins with _"#"_.
> 1. If _specifier_ is exactly equal to _"#"_ or starts with _"#/"_, then
@@ -1857,22 +1771,79 @@ The resolver can throw the following errors:
> 1. Let _packageURL_ be the result of **READ_PACKAGE_SCOPE**(_parentURL_).
> 1. If _packageURL_ is not **null**, then
> 1. Let _pjson_ be the result of **READ_PACKAGE_JSON**(_packageURL_).
> 1. If _pjson.imports is a non-null Object, then
> 1. Let _imports_ be _pjson.imports_.
> 1. If _specifier_ is a key of _imports_, then
> 1. Let _target_ be the value of _imports\[specifier\]_.
> 1. Return **PACKAGE_TARGET_RESOLVE**(_packageURL_, _target_,
> _""_, **true**, _defaultEnv_).
> 1. Let _directoryKeys_ be the list of keys of _imports_ ending in
> _"/"_, sorted by length descending.
> 1. For each key _directory_ in _directoryKeys_, do
> 1. If _specifier_ starts with _directory_, then
> 1. Let _target_ be the value of _imports\[directory\]_.
> 1. Let _subpath_ be the substring of _target_ starting at the
> index of the length of _directory_.
> 1. Return **PACKAGE_TARGET_RESOLVE**(_packageURL_, _target_,
> _subpath_, **true**, _defaultEnv_).
> 1. Return **null**.
> 1. If _pjson.imports_ is a non-null Object, then
> 1. Let _resolvedMatch_ be the result of
> **PACKAGE_IMPORTS_EXPORTS_RESOLVE**(_specifier_, _pjson.imports_,
> _packageURL_, **true**, _conditions_).
> 1. If _resolvedMatch_._resolve_ is not **null** or **undefined**, then
> 1. Return _resolvedMatch_.
> 1. Throw a _Package Import Not Defined_ error.
**PACKAGE_IMPORTS_EXPORTS_RESOLVE**(_matchKey_, _matchObj_, _packageURL_,
_isImports_, _conditions_)
> 1. If _matchKey_ is a key of _matchObj_, and does not end in _"*"_, then
> 1. Let _target_ be the value of _matchObj_\[_matchKey_\].
> 1. Let _resolved_ be the result of **PACKAGE_TARGET_RESOLVE**(
> _packageURL_, _target_, _""_, _isImports_, _conditions_).
> 1. Return the object _{ resolved, exact: **true** }_.
> 1. Let _expansionKeys_ be the list of keys of _matchObj_ ending in _"/"_,
> sorted by length descending.
> 1. For each key _expansionKey_ in _expansionKeys_, do
> 1. If _matchKey_ starts with _expansionKey_, then
> 1. Let _target_ be the value of _matchObj_\[_expansionKey_\].
> 1. Let _subpath_ be the substring of _matchKey_ starting at the
> index of the length of _expansionKey_.
> 1. Let _resolved_ be the result of **PACKAGE_TARGET_RESOLVE**(
> _packageURL_, _target_, _subpath_, _isImports_, _conditions_).
> 1. Return the object _{ resolved, exact: **false** }_.
> 1. Return the object _{ resolved: **null**, exact: **true** }_.
**PACKAGE_TARGET_RESOLVE**(_packageURL_, _target_, _subpath_, _internal_,
_conditions_)
> 1. If _target_ is a String, then
> 1. If _subpath_ has non-zero length and _target_ does not end with _"/"_,
> throw an _Invalid Module Specifier_ error.
> 1. If _target_ does not start with _"./"_, then
> 1. If _internal_ is **true** and _target_ does not start with _"../"_ or
> _"/"_ and is not a valid URL, then
> 1. Return **PACKAGE_RESOLVE**(_target_ + _subpath_,
> _packageURL_ + _"/"_)_.
> 1. Otherwise, throw an _Invalid Package Target_ error.
> 1. If _target_ split on _"/"_ or _"\\"_ contains any _"."_, _".."_ or
> _"node_modules"_ segments after the first segment, throw an
> _Invalid Module Specifier_ error.
> 1. Let _resolvedTarget_ be the URL resolution of the concatenation of
> _packageURL_ and _target_.
> 1. Assert: _resolvedTarget_ is contained in _packageURL_.
> 1. If _subpath_ split on _"/"_ or _"\\"_ contains any _"."_, _".."_ or
> _"node_modules"_ segments, throw an _Invalid Module Specifier_ error.
> 1. Return the URL resolution of the concatenation of _subpath_ and
> _resolvedTarget_.
> 1. Otherwise, if _target_ is a non-null Object, then
> 1. If _exports_ contains any index property keys, as defined in ECMA-262
> [6.1.7 Array Index][], throw an _Invalid Package Configuration_ error.
> 1. For each property _p_ of _target_, in object insertion order as,
> 1. If _p_ equals _"default"_ or _conditions_ contains an entry for _p_,
> then
> 1. Let _targetValue_ be the value of the _p_ property in _target_.
> 1. Let _resolved_ be the result of **PACKAGE_TARGET_RESOLVE**(
> _packageURL_, _targetValue_, _subpath_, _internal_, _conditions_).
> 1. If _resolved_ is equal to **undefined**, continue the loop.
> 1. Return _resolved_.
> 1. Return **undefined**.
> 1. Otherwise, if _target_ is an Array, then
> 1. If _target.length is zero, return **null**.
> 1. For each item _targetValue_ in _target_, do
> 1. Let _resolved_ be the result of **PACKAGE_TARGET_RESOLVE**(
> _packageURL_, _targetValue_, _subpath_, _internal_, _conditions_),
> continuing the loop on any _Invalid Package Target_ error.
> 1. If _resolved_ is **undefined**, continue the loop.
> 1. Return _resolved_.
> 1. Return or throw the last fallback resolution **null** return or error.
> 1. Otherwise, if _target_ is _null_, return **null**.
> 1. Otherwise throw an _Invalid Package Target_ error.
**ESM_FORMAT**(_url_)

View File

@@ -161,10 +161,10 @@ require(X) from module at path Y
b. LOAD_AS_DIRECTORY(Y + X)
c. THROW "not found"
4. If X begins with '#'
a. LOAD_INTERAL_IMPORT(X, Y)
4. LOAD_SELF_REFERENCE(X, Y)
5. LOAD_NODE_MODULES(X, dirname(Y))
6. THROW "not found"
a. LOAD_PACKAGE_IMPORTS(X, dirname(Y))
5. LOAD_PACKAGE_SELF(X, dirname(Y))
6. LOAD_NODE_MODULES(X, dirname(Y))
7. THROW "not found"
LOAD_AS_FILE(X)
1. If X is a file, load X as its file extension format. STOP
@@ -191,7 +191,7 @@ LOAD_AS_DIRECTORY(X)
LOAD_NODE_MODULES(X, START)
1. let DIRS = NODE_MODULES_PATHS(START)
2. for each DIR in DIRS:
a. LOAD_PACKAGE_EXPORTS(DIR, X)
a. LOAD_PACKAGE_EXPORTS(X, DIR)
b. LOAD_AS_FILE(DIR/X)
c. LOAD_AS_DIRECTORY(DIR/X)
@@ -206,47 +206,45 @@ NODE_MODULES_PATHS(START)
d. let I = I - 1
5. return DIRS
LOAD_SELF_REFERENCE(X, START)
1. Find the closest package scope to START.
LOAD_PACKAGE_IMPORTS(X, DIR)
1. Find the closest package scope SCOPE to DIR.
2. If no scope was found, return.
3. If the `package.json` has no "exports", return.
4. If the name in `package.json` is a prefix of X, then
a. Load the remainder of X relative to this package as if it was
loaded via `LOAD_NODE_MODULES` with a name in `package.json`.
3. If the SCOPE/package.json "imports" is null or undefined, return.
4. let MATCH = PACKAGE_IMPORTS_RESOLVE(X, pathToFileURL(SCOPE),
["node", "require"]) defined in the ESM resolver.
5. RESOLVE_ESM_MATCH(MATCH).
LOAD_PACKAGE_EXPORTS(DIR, X)
1. Try to interpret X as a combination of name and subpath where the name
LOAD_PACKAGE_EXPORTS(X, DIR)
1. Try to interpret X as a combination of NAME and SUBPATH where the name
may have a @scope/ prefix and the subpath begins with a slash (`/`).
2. If X does not match this pattern or DIR/name/package.json is not a file,
2. If X does not match this pattern or DIR/NAME/package.json is not a file,
return.
3. Parse DIR/name/package.json, and look for "exports" field.
3. Parse DIR/NAME/package.json, and look for "exports" field.
4. If "exports" is null or undefined, return.
5. If "exports" is an object with some keys starting with "." and some keys
not starting with ".", throw "invalid config".
6. If "exports" is a string, or object with no keys starting with ".", treat
it as having that value as its "." object property.
7. If subpath is "." and "exports" does not have a "." entry, return.
8. Find the longest key in "exports" that the subpath starts with.
9. If no such key can be found, throw "not found".
10. let RESOLVED =
fileURLToPath(PACKAGE_EXPORTS_TARGET_RESOLVE(pathToFileURL(DIR/name),
exports[key], subpath.slice(key.length), ["node", "require"])), as defined
in the ESM resolver.
11. If key ends with "/":
a. LOAD_AS_FILE(RESOLVED)
b. LOAD_AS_DIRECTORY(RESOLVED)
12. Otherwise
a. If RESOLVED is a file, load it as its file extension format. STOP
13. Throw "not found"
5. let MATCH = PACKAGE_EXPORTS_RESOLVE(pathToFileURL(DIR/NAME), "." + SUBPATH,
`package.json` "exports", ["node", "require"]) defined in the ESM resolver.
6. RESOLVE_ESM_MATCH(MATCH)
LOAD_INTERNAL_IMPORT(X, START)
1. Find the closest package scope to START.
2. If no scope was found or the `package.json` has no "imports", return.
3. let RESOLVED =
fileURLToPath(PACKAGE_INTERNAL_RESOLVE(X, pathToFileURL(START)), as defined
in the ESM resolver.
4. If RESOLVED is not a valid file, throw "not found"
5. Load RESOLVED as its file extension format. STOP
LOAD_PACKAGE_SELF(X, DIR)
1. Find the closest package scope SCOPE to DIR.
2. If no scope was found, return.
3. If the SCOPE/package.json "exports" is null or undefined, return.
4. If the SCOPE/package.json "name" is not the first segment of X, return.
5. let MATCH = PACKAGE_EXPORTS_RESOLVE(pathToFileURL(SCOPE),
"." + X.slice("name".length), `package.json` "exports", ["node", "require"])
defined in the ESM resolver.
6. RESOLVE_ESM_MATCH(MATCH)
RESOLVE_ESM_MATCH(MATCH)
1. let { RESOLVED, EXACT } = MATCH
2. let RESOLVED_PATH = fileURLToPath(RESOLVED)
3. If EXACT is true,
a. If the file at RESOLVED_PATH exists, load RESOLVED_PATH as its extension
format. STOP
4. Otherwise, if EXACT is false,
a. LOAD_AS_FILE(RESOLVED_PATH)
b. LOAD_AS_DIRECTORY(RESOLVED_PATH)
5. THROW "not found"
```
## Caching

View File

@@ -26,8 +26,6 @@ const {
WeakMap,
} = primordials;
const sep = process.platform === 'win32' ? '\\' : '/';
const messages = new Map();
const codes = {};
@@ -1120,10 +1118,9 @@ E('ERR_INVALID_OPT_VALUE', (name, value) =>
RangeError);
E('ERR_INVALID_OPT_VALUE_ENCODING',
'The value "%s" is invalid for option "encoding"', TypeError);
E('ERR_INVALID_PACKAGE_CONFIG', (path, message, hasMessage = true) => {
if (hasMessage)
return `Invalid package config ${path}${sep}package.json, ${message}`;
return `Invalid JSON in ${path} imported from ${message}`;
E('ERR_INVALID_PACKAGE_CONFIG', (path, base, message) => {
return `Invalid package config ${path}${base ? ` imported from ${base}` :
''}${message ? `. ${message}` : ''}`;
}, Error);
E('ERR_INVALID_PACKAGE_TARGET',
(pkgPath, key, target, isImport = false, base = undefined) => {

View File

@@ -33,13 +33,11 @@ const {
Error,
JSONParse,
Map,
Number,
ObjectCreate,
ObjectDefineProperty,
ObjectFreeze,
ObjectGetOwnPropertyDescriptor,
ObjectGetPrototypeOf,
ObjectIs,
ObjectKeys,
ObjectPrototypeHasOwnProperty,
ObjectSetPrototypeOf,
@@ -49,7 +47,6 @@ const {
SafeWeakMap,
SafeSet,
String,
StringPrototypeIndexOf,
StringPrototypeMatch,
StringPrototypeSlice,
StringPrototypeStartsWith,
@@ -94,10 +91,7 @@ let hasLoadedAnyUserCJSModule = false;
const {
ERR_INVALID_ARG_VALUE,
ERR_INVALID_OPT_VALUE,
ERR_INVALID_PACKAGE_CONFIG,
ERR_INVALID_PACKAGE_TARGET,
ERR_INVALID_MODULE_SPECIFIER,
ERR_PACKAGE_PATH_NOT_EXPORTED,
ERR_REQUIRE_ESM
} = require('internal/errors').codes;
const { validateString } = require('internal/validators');
@@ -117,7 +111,8 @@ const asyncESM = require('internal/process/esm_loader');
const { kEvaluated } = internalBinding('module_wrap');
const {
encodedSepRegEx,
packageInternalResolve
packageExportsResolve,
packageImportsResolve
} = require('internal/modules/esm/resolve');
const isWindows = process.platform === 'win32';
@@ -319,18 +314,8 @@ function readPackageScope(checkPath) {
return false;
}
function readPackageMain(requestPath) {
const pkg = readPackage(requestPath);
return pkg ? pkg.main : undefined;
}
function readPackageExports(requestPath) {
const pkg = readPackage(requestPath);
return pkg ? pkg.exports : undefined;
}
function tryPackage(requestPath, exts, isMain, originalPath) {
const pkg = readPackageMain(requestPath);
const pkg = readPackage(requestPath)?.main;
if (!pkg) {
return tryExtensions(path.resolve(requestPath, 'index'), exts, isMain);
@@ -435,108 +420,28 @@ function trySelfParentPath(parent) {
function trySelf(parentPath, request) {
if (!parentPath) return false;
const { data: pkg, path: basePath } = readPackageScope(parentPath) || {};
const { data: pkg, path: pkgPath } = readPackageScope(parentPath) || {};
if (!pkg || pkg.exports === undefined) return false;
if (typeof pkg.name !== 'string') return false;
let expansion;
if (request === pkg.name) {
expansion = '';
expansion = '.';
} else if (StringPrototypeStartsWith(request, `${pkg.name}/`)) {
expansion = StringPrototypeSlice(request, pkg.name.length);
expansion = '.' + StringPrototypeSlice(request, pkg.name.length);
} else {
return false;
}
const fromExports = applyExports(basePath, expansion);
if (fromExports) {
return tryFile(fromExports, false);
try {
return finalizeEsmResolution(packageExportsResolve(
pathToFileURL(pkgPath + '/package.json'), expansion, pkg,
pathToFileURL(parentPath), cjsConditions), request, parentPath, pkgPath);
} catch (e) {
if (e.code === 'ERR_MODULE_NOT_FOUND')
throw createEsmNotFoundErr(request, pkgPath + '/package.json');
throw e;
}
assert(fromExports !== false);
}
function isConditionalDotExportSugar(exports, basePath) {
if (typeof exports === 'string')
return true;
if (ArrayIsArray(exports))
return true;
if (typeof exports !== 'object')
return false;
let isConditional = false;
let firstCheck = true;
for (const key of ObjectKeys(exports)) {
const curIsConditional = key[0] !== '.';
if (firstCheck) {
firstCheck = false;
isConditional = curIsConditional;
} else if (isConditional !== curIsConditional) {
throw new ERR_INVALID_PACKAGE_CONFIG(basePath, '"exports" cannot ' +
'contain some keys starting with \'.\' and some not. The exports ' +
'object must either be an object of package subpath keys or an ' +
'object of main entry condition name keys only.');
}
}
return isConditional;
}
function applyExports(basePath, expansion) {
const mappingKey = `.${expansion}`;
let pkgExports = readPackageExports(basePath);
if (pkgExports === undefined || pkgExports === null)
return false;
if (isConditionalDotExportSugar(pkgExports, basePath))
pkgExports = { '.': pkgExports };
if (typeof pkgExports === 'object') {
if (ObjectPrototypeHasOwnProperty(pkgExports, mappingKey)) {
const mapping = pkgExports[mappingKey];
const resolved = resolveExportsTarget(
pathToFileURL(basePath + '/'), mapping, '', mappingKey);
if (resolved === null || resolved === undefined)
throw new ERR_PACKAGE_PATH_NOT_EXPORTED(
basePath, mappingKey);
return resolved;
}
let dirMatch = '';
for (const candidateKey of ObjectKeys(pkgExports)) {
if (candidateKey[candidateKey.length - 1] !== '/') continue;
if (candidateKey.length > dirMatch.length &&
StringPrototypeStartsWith(mappingKey, candidateKey)) {
dirMatch = candidateKey;
}
}
if (dirMatch !== '') {
const mapping = pkgExports[dirMatch];
const subpath = StringPrototypeSlice(mappingKey, dirMatch.length);
const resolved = resolveExportsTarget(pathToFileURL(basePath + '/'),
mapping, subpath, mappingKey);
if (resolved === null || resolved === undefined)
throw new ERR_PACKAGE_PATH_NOT_EXPORTED(
basePath, mappingKey + subpath);
// Extension searching for folder exports only
const rc = stat(resolved);
if (rc === 0) return resolved;
if (!(RegExpPrototypeTest(trailingSlashRegex, resolved))) {
const exts = ObjectKeys(Module._extensions);
const filename = tryExtensions(resolved, exts, false);
if (filename) return filename;
}
if (rc === 1) {
const exts = ObjectKeys(Module._extensions);
const filename = tryPackage(resolved, exts, false,
basePath + expansion);
if (filename) return filename;
}
// Undefined means not found
return;
}
}
throw new ERR_PACKAGE_PATH_NOT_EXPORTED(basePath, mappingKey);
}
// This only applies to requests of a specific form:
@@ -547,107 +452,21 @@ function resolveExports(nmPath, request) {
// The implementation's behavior is meant to mirror resolution in ESM.
const [, name, expansion = ''] =
StringPrototypeMatch(request, EXPORTS_PATTERN) || [];
if (!name) {
return false;
if (!name)
return;
const pkgPath = path.resolve(nmPath, name);
const pkg = readPackage(pkgPath);
if (pkg && pkg.exports !== null && pkg.exports !== undefined) {
try {
return finalizeEsmResolution(packageExportsResolve(
pathToFileURL(pkgPath + '/package.json'), '.' + expansion, pkg, null,
cjsConditions), request, null, pkgPath);
} catch (e) {
if (e.code === 'ERR_MODULE_NOT_FOUND')
throw createEsmNotFoundErr(request, pkgPath + '/package.json');
throw e;
}
}
const basePath = path.resolve(nmPath, name);
const fromExports = applyExports(basePath, expansion);
if (fromExports) {
return tryFile(fromExports, false);
}
return fromExports;
}
function isArrayIndex(p) {
assert(typeof p === 'string');
const n = Number(p);
if (String(n) !== p)
return false;
if (ObjectIs(n, +0))
return true;
if (!Number.isInteger(n))
return false;
return n >= 0 && n < (2 ** 32) - 1;
}
function resolveExportsTarget(baseUrl, target, subpath, mappingKey) {
if (typeof target === 'string') {
let resolvedTarget, resolvedTargetPath;
const pkgPathPath = baseUrl.pathname;
if (StringPrototypeStartsWith(target, './')) {
resolvedTarget = new URL(target, baseUrl);
resolvedTargetPath = resolvedTarget.pathname;
if (!StringPrototypeStartsWith(resolvedTargetPath, pkgPathPath) ||
StringPrototypeIndexOf(resolvedTargetPath, '/node_modules/',
pkgPathPath.length - 1) !== -1)
resolvedTarget = undefined;
}
if (subpath.length > 0 && target[target.length - 1] !== '/')
resolvedTarget = undefined;
if (resolvedTarget === undefined)
throw new ERR_INVALID_PACKAGE_TARGET(baseUrl.pathname, mappingKey,
target);
const resolved = new URL(subpath, resolvedTarget);
const resolvedPath = resolved.pathname;
if (StringPrototypeStartsWith(resolvedPath, resolvedTargetPath) &&
StringPrototypeIndexOf(resolvedPath, '/node_modules/',
pkgPathPath.length - 1) === -1) {
if (StringPrototypeMatch(resolvedPath, encodedSepRegEx))
throw new ERR_INVALID_MODULE_SPECIFIER(
resolvedPath, 'must not include encoded "/" or "\\" characters',
fileURLToPath(baseUrl));
return fileURLToPath(resolved);
}
const reason = 'request is not a valid subpath for the "exports" ' +
`resolution of ${baseUrl.pathname}package.json`;
throw new ERR_INVALID_MODULE_SPECIFIER(mappingKey + subpath, reason);
} else if (ArrayIsArray(target)) {
if (target.length === 0)
return null;
let lastException;
for (const targetValue of target) {
let resolved;
try {
resolved = resolveExportsTarget(baseUrl, targetValue, subpath,
mappingKey);
} catch (e) {
lastException = e;
if (e.code !== 'ERR_INVALID_PACKAGE_TARGET')
throw e;
}
if (resolved === undefined)
continue;
if (resolved === null) {
lastException = null;
continue;
}
return resolved;
}
// Throw last fallback error
if (lastException === undefined || lastException === null)
return lastException;
throw lastException;
} else if (typeof target === 'object' && target !== null) {
const keys = ObjectKeys(target);
if (keys.some(isArrayIndex)) {
throw new ERR_INVALID_PACKAGE_CONFIG(baseUrl, '"exports" cannot ' +
'contain numeric property keys.');
}
for (const p of keys) {
if (cjsConditions.has(p) || p === 'default') {
const resolved = resolveExportsTarget(baseUrl, target[p], subpath,
mappingKey);
if (resolved === undefined)
continue;
return resolved;
}
}
return undefined;
} else if (target === null) {
return null;
}
throw new ERR_INVALID_PACKAGE_TARGET(baseUrl.pathname, mappingKey, target);
}
const trailingSlashRegex = /(?:^|\/)\.?\.$/;
@@ -680,12 +499,8 @@ Module._findPath = function(request, paths, isMain) {
if (!absoluteRequest) {
const exportsResolved = resolveExports(curPath, request);
// Undefined means not found, false means no exports
if (exportsResolved === undefined)
break;
if (exportsResolved) {
if (exportsResolved)
return exportsResolved;
}
}
const basePath = path.resolve(curPath, request);
@@ -1050,19 +865,14 @@ Module._resolveFilename = function(request, parent, isMain, options) {
if (pkg.data && pkg.data.imports !== null &&
pkg.data.imports !== undefined) {
try {
const resolved = packageInternalResolve(
request, pathToFileURL(parent.filename), cjsConditions);
return fileURLToPath(resolved);
} catch (err) {
if (err.code === 'ERR_MODULE_NOT_FOUND') {
// eslint-disable-next-line no-restricted-syntax
const err = new Error(`Cannot find module '${request}'`);
err.code = 'MODULE_NOT_FOUND';
err.path = path.resolve(pkg.path, 'package.json');
// TODO(BridgeAR): Add the requireStack as well.
throw err;
}
throw err;
return finalizeEsmResolution(
packageImportsResolve(request, pathToFileURL(parent.filename),
cjsConditions), request, parent.filename,
pkg.path);
} catch (e) {
if (e.code === 'ERR_MODULE_NOT_FOUND')
throw createEsmNotFoundErr(request);
throw e;
}
}
}
@@ -1098,6 +908,34 @@ Module._resolveFilename = function(request, parent, isMain, options) {
throw err;
};
function finalizeEsmResolution(match, request, parentPath, pkgPath) {
const { resolved, exact } = match;
if (StringPrototypeMatch(resolved, encodedSepRegEx))
throw new ERR_INVALID_MODULE_SPECIFIER(
resolved, 'must not include encoded "/" or "\\" characters', parentPath);
const filename = fileURLToPath(resolved);
let actual = tryFile(filename);
if (!exact && !actual) {
const exts = ObjectKeys(Module._extensions);
actual = tryExtensions(filename, exts, false) ||
tryPackage(filename, exts, false, request);
}
if (actual)
return actual;
const err = createEsmNotFoundErr(filename,
path.resolve(pkgPath, 'package.json'));
throw err;
}
function createEsmNotFoundErr(request, path) {
// eslint-disable-next-line no-restricted-syntax
const err = new Error(`Cannot find module '${request}'`);
err.code = 'MODULE_NOT_FOUND';
if (path)
err.path = path;
// TODO(BridgeAR): Add the requireStack as well.
return err;
}
// Given a file name, pass it to the proper extension handler.
Module.prototype.load = function(filename) {

View File

@@ -15,7 +15,6 @@ const {
SafeSet,
String,
StringPrototypeEndsWith,
StringPrototypeIncludes,
StringPrototypeIndexOf,
StringPrototypeReplace,
StringPrototypeSlice,
@@ -78,14 +77,6 @@ function tryStatSync(path) {
}
}
/**
*
* '/foo/package.json' -> '/foo'
*/
function removePackageJsonFromPath(path) {
return StringPrototypeSlice(path, 0, path.length - 13);
}
function getPackageConfig(path) {
const existing = packageJSONCache.get(path);
if (existing !== undefined) {
@@ -110,8 +101,7 @@ function getPackageConfig(path) {
try {
packageJSON = JSONParse(source);
} catch (error) {
const errorPath = removePackageJsonFromPath(path);
throw new ERR_INVALID_PACKAGE_CONFIG(errorPath, error.message, true);
throw new ERR_INVALID_PACKAGE_CONFIG(path, null, error.message);
}
let { imports, main, name, type } = packageJSON;
@@ -177,7 +167,7 @@ function fileExists(url) {
return tryStatSync(fileURLToPath(url)).isFile();
}
function legacyMainResolve(packageJSONUrl, packageConfig) {
function legacyMainResolve(packageJSONUrl, packageConfig, base) {
let guess;
if (packageConfig.main !== undefined) {
// Note: fs check redundances will be handled by Descriptor cache here.
@@ -222,7 +212,8 @@ function legacyMainResolve(packageJSONUrl, packageConfig) {
return guess;
}
// Not found.
return undefined;
throw new ERR_MODULE_NOT_FOUND(
fileURLToPath(new URL('.', packageJSONUrl)), fileURLToPath(base));
}
function resolveExtensionsWithTryExactName(search) {
@@ -246,35 +237,34 @@ function resolveIndex(search) {
const encodedSepRegEx = /%2F|%2C/i;
function finalizeResolution(resolved, base) {
if (getOptionValue('--experimental-specifier-resolution') === 'node') {
let file = resolveExtensionsWithTryExactName(resolved);
if (file !== undefined) return file;
if (!StringPrototypeEndsWith(resolved.pathname, '/')) {
file = resolveIndex(new URL(`${resolved.pathname}/`, base));
} else {
file = resolveIndex(resolved);
}
if (file !== undefined) return file;
throw new ERR_MODULE_NOT_FOUND(
resolved.pathname, fileURLToPath(base), 'module');
}
if (RegExpPrototypeTest(encodedSepRegEx, resolved.pathname))
throw new ERR_INVALID_MODULE_SPECIFIER(
resolved.pathname, 'must not include encoded "/" or "\\" characters',
fileURLToPath(base));
const path = fileURLToPath(resolved);
const stats = tryStatSync(path);
if (getOptionValue('--experimental-specifier-resolution') === 'node') {
let file = resolveExtensionsWithTryExactName(resolved);
if (file !== undefined) return file;
if (!StringPrototypeEndsWith(path, '/')) {
file = resolveIndex(new URL(`${resolved}/`));
if (file !== undefined) return file;
} else {
return resolveIndex(resolved) || resolved;
}
throw new ERR_MODULE_NOT_FOUND(
resolved.pathname, fileURLToPath(base), 'module');
}
const stats = tryStatSync(StringPrototypeEndsWith(path, '/') ?
StringPrototypeSlice(path, -1) : path);
if (stats.isDirectory()) {
const err = new ERR_UNSUPPORTED_DIR_IMPORT(
path || resolved.pathname, fileURLToPath(base));
const err = new ERR_UNSUPPORTED_DIR_IMPORT(path, fileURLToPath(base));
err.url = String(resolved);
throw err;
} else if (!stats.isFile()) {
throw new ERR_MODULE_NOT_FOUND(
path || resolved.pathname, fileURLToPath(base), 'module');
path || resolved.pathname, base && fileURLToPath(base), 'module');
}
return resolved;
@@ -288,14 +278,15 @@ function throwImportNotDefined(specifier, packageJSONUrl, base) {
function throwExportsNotFound(subpath, packageJSONUrl, base) {
throw new ERR_PACKAGE_PATH_NOT_EXPORTED(
fileURLToPath(new URL('.', packageJSONUrl)), subpath, fileURLToPath(base));
fileURLToPath(new URL('.', packageJSONUrl)), subpath,
base && fileURLToPath(base));
}
function throwInvalidSubpath(subpath, packageJSONUrl, internal, base) {
const reason = `request is not a valid subpath for the "${internal ?
'imports' : 'exports'}" resolution of ${fileURLToPath(packageJSONUrl)}${
base ? ` imported from ${base}` : ''}`;
throw new ERR_INVALID_MODULE_SPECIFIER(subpath, reason, fileURLToPath(base));
'imports' : 'exports'}" resolution of ${fileURLToPath(packageJSONUrl)}`;
throw new ERR_INVALID_MODULE_SPECIFIER(subpath, reason,
base && fileURLToPath(base));
}
function throwInvalidPackageTarget(
@@ -307,44 +298,46 @@ function throwInvalidPackageTarget(
}
throw new ERR_INVALID_PACKAGE_TARGET(
fileURLToPath(new URL('.', packageJSONUrl)), subpath, target,
internal, fileURLToPath(base));
internal, base && fileURLToPath(base));
}
const invalidSegmentRegEx = /(^|\\|\/)(\.\.?|node_modules)(\\|\/|$)/;
function resolvePackageTargetString(
target, subpath, match, packageJSONUrl, base, internal, conditions) {
if (subpath !== '' && target[target.length - 1] !== '/')
throwInvalidPackageTarget(match, target, packageJSONUrl, internal, base);
if (!target.startsWith('./')) {
if (internal && !target.startsWith('../') && !target.startsWith('/')) {
if (!StringPrototypeStartsWith(target, './')) {
if (internal && !StringPrototypeStartsWith(target, '../') &&
!StringPrototypeStartsWith(target, '/')) {
let isURL = false;
try {
new URL(target);
isURL = true;
} catch {}
if (!isURL)
return packageResolve(target + subpath, base, conditions);
return packageResolve(target + subpath, packageJSONUrl, conditions);
}
throwInvalidPackageTarget(match, target, packageJSONUrl, internal, base);
}
if (RegExpPrototypeTest(invalidSegmentRegEx, StringPrototypeSlice(target, 2)))
throwInvalidPackageTarget(match, target, packageJSONUrl, internal, base);
const resolved = new URL(target, packageJSONUrl);
const resolvedPath = resolved.pathname;
const packagePath = new URL('.', packageJSONUrl).pathname;
if (!StringPrototypeStartsWith(resolvedPath, packagePath) ||
StringPrototypeIncludes(
resolvedPath, '/node_modules/', packagePath.length - 1))
if (!StringPrototypeStartsWith(resolvedPath, packagePath))
throwInvalidPackageTarget(match, target, packageJSONUrl, internal, base);
if (subpath === '') return resolved;
const subpathResolved = new URL(subpath, resolved);
const subpathResolvedPath = subpathResolved.pathname;
if (!StringPrototypeStartsWith(subpathResolvedPath, resolvedPath) ||
StringPrototypeIncludes(subpathResolvedPath,
'/node_modules/', packagePath.length - 1))
if (RegExpPrototypeTest(invalidSegmentRegEx, subpath))
throwInvalidSubpath(match + subpath, packageJSONUrl, internal, base);
return subpathResolved;
return new URL(subpath, resolved);
}
/**
@@ -357,12 +350,12 @@ function isArrayIndex(key) {
return keyNum >= 0 && keyNum < 0xFFFF_FFFF;
}
function resolvePackageTarget(
packageJSONUrl, target, subpath, packageSubpath, base, internal, conditions) {
function resolvePackageTarget(packageJSONUrl, target, subpath, packageSubpath,
base, internal, conditions) {
if (typeof target === 'string') {
return finalizeResolution(resolvePackageTargetString(
return resolvePackageTargetString(
target, subpath, packageSubpath, packageJSONUrl, base, internal,
conditions), base);
conditions);
} else if (ArrayIsArray(target)) {
if (target.length === 0)
return null;
@@ -387,7 +380,7 @@ function resolvePackageTarget(
lastException = null;
continue;
}
return finalizeResolution(resolved, base);
return resolved;
}
if (lastException === undefined || lastException === null)
return lastException;
@@ -398,8 +391,8 @@ function resolvePackageTarget(
const key = keys[i];
if (isArrayIndex(key)) {
throw new ERR_INVALID_PACKAGE_CONFIG(
fileURLToPath(packageJSONUrl),
'"exports" cannot contain numeric property keys');
fileURLToPath(packageJSONUrl), base,
'"exports" cannot contain numeric property keys.');
}
}
for (let i = 0; i < keys.length; i++) {
@@ -436,7 +429,7 @@ function isConditionalExportsMainSugar(exports, packageJSONUrl, base) {
isConditionalSugar = curIsConditionalSugar;
} else if (isConditionalSugar !== curIsConditionalSugar) {
throw new ERR_INVALID_PACKAGE_CONFIG(
fileURLToPath(packageJSONUrl),
fileURLToPath(packageJSONUrl), base,
'"exports" cannot contain some keys starting with \'.\' and some not.' +
' The exports object must either be an object of package subpath keys' +
' or an object of main entry condition name keys only.');
@@ -445,44 +438,6 @@ function isConditionalExportsMainSugar(exports, packageJSONUrl, base) {
return isConditionalSugar;
}
function packageMainResolve(packageJSONUrl, packageConfig, base, conditions) {
if (packageConfig.exists) {
const exports = packageConfig.exports;
if (exports !== undefined) {
if (isConditionalExportsMainSugar(exports, packageJSONUrl, base)) {
const resolved = resolvePackageTarget(packageJSONUrl, exports, '', '',
base, false, conditions);
if (resolved === null || resolved === undefined)
throwExportsNotFound('.', packageJSONUrl, base);
return resolved;
} else if (typeof exports === 'object' && exports !== null) {
const target = exports['.'];
if (target !== undefined) {
const resolved = resolvePackageTarget(packageJSONUrl, target, '', '',
base, false, conditions);
if (resolved === null || resolved === undefined)
throwExportsNotFound('.', packageJSONUrl, base);
return resolved;
}
}
throw new ERR_PACKAGE_PATH_NOT_EXPORTED(packageJSONUrl, '.');
}
if (getOptionValue('--experimental-specifier-resolution') === 'node') {
if (packageConfig.main !== undefined) {
return finalizeResolution(
new URL(packageConfig.main, packageJSONUrl), base);
}
return finalizeResolution(
new URL('index', packageJSONUrl), base);
}
return legacyMainResolve(packageJSONUrl, packageConfig);
}
throw new ERR_MODULE_NOT_FOUND(
fileURLToPath(new URL('.', packageJSONUrl)), fileURLToPath(base));
}
/**
* @param {URL} packageJSONUrl
* @param {string} packageSubpath
@@ -493,28 +448,27 @@ function packageMainResolve(packageJSONUrl, packageConfig, base, conditions) {
*/
function packageExportsResolve(
packageJSONUrl, packageSubpath, packageConfig, base, conditions) {
const exports = packageConfig.exports;
if (exports === undefined ||
isConditionalExportsMainSugar(exports, packageJSONUrl, base)) {
throwExportsNotFound(packageSubpath, packageJSONUrl, base);
}
let exports = packageConfig.exports;
if (isConditionalExportsMainSugar(exports, packageJSONUrl, base))
exports = { '.': exports };
if (ObjectPrototypeHasOwnProperty(exports, packageSubpath)) {
const target = exports[packageSubpath];
const resolved = resolvePackageTarget(
packageJSONUrl, target, '', packageSubpath, base, false, conditions);
packageJSONUrl, target, '', packageSubpath, base, false, conditions
);
if (resolved === null || resolved === undefined)
throwExportsNotFound(packageSubpath, packageJSONUrl, base);
return finalizeResolution(resolved, base);
return { resolved, exact: true };
}
let bestMatch = '';
const keys = ObjectGetOwnPropertyNames(exports);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
if (key[key.length - 1] !== '/') continue;
if (StringPrototypeStartsWith(packageSubpath, key) &&
key.length > bestMatch.length) {
if (key[key.length - 1] === '/' &&
StringPrototypeStartsWith(packageSubpath, key) &&
key.length > bestMatch.length) {
bestMatch = key;
}
}
@@ -522,18 +476,18 @@ function packageExportsResolve(
if (bestMatch) {
const target = exports[bestMatch];
const subpath = StringPrototypeSubstr(packageSubpath, bestMatch.length);
const resolved = resolvePackageTarget(
packageJSONUrl, target, subpath, bestMatch, base, false, conditions);
const resolved = resolvePackageTarget(packageJSONUrl, target, subpath,
bestMatch, base, false, conditions);
if (resolved === null || resolved === undefined)
throwExportsNotFound(packageSubpath, packageJSONUrl, base);
return finalizeResolution(resolved, base);
return { resolved, exact: false };
}
throwExportsNotFound(packageSubpath, packageJSONUrl, base);
}
function packageInternalResolve(name, base, conditions) {
if (name === '#' || name.startsWith('#/')) {
function packageImportsResolve(name, base, conditions) {
if (name === '#' || StringPrototypeStartsWith(name, '#/')) {
const reason = 'is not a valid internal imports specifier name';
throw new ERR_INVALID_MODULE_SPECIFIER(name, reason, fileURLToPath(base));
}
@@ -545,17 +499,18 @@ function packageInternalResolve(name, base, conditions) {
if (imports) {
if (ObjectPrototypeHasOwnProperty(imports, name)) {
const resolved = resolvePackageTarget(
packageJSONUrl, imports[name], '', name, base, true, conditions);
packageJSONUrl, imports[name], '', name, base, true, conditions
);
if (resolved !== null)
return finalizeResolution(resolved, base);
return { resolved, exact: true };
} else {
let bestMatch = '';
const keys = ObjectGetOwnPropertyNames(imports);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
if (key[key.length - 1] !== '/') continue;
if (StringPrototypeStartsWith(name, key) &&
key.length > bestMatch.length) {
if (key[key.length - 1] === '/' &&
StringPrototypeStartsWith(name, key) &&
key.length > bestMatch.length) {
bestMatch = key;
}
}
@@ -564,10 +519,9 @@ function packageInternalResolve(name, base, conditions) {
const target = imports[bestMatch];
const subpath = StringPrototypeSubstr(name, bestMatch.length);
const resolved = resolvePackageTarget(
packageJSONUrl, target, subpath, bestMatch, base, true,
conditions);
packageJSONUrl, target, subpath, bestMatch, base, true, conditions);
if (resolved !== null)
return finalizeResolution(resolved, base);
return { resolved, exact: false };
}
}
}
@@ -617,23 +571,18 @@ function packageResolve(specifier, base, conditions) {
specifier, 'is not a valid package name', fileURLToPath(base));
}
const packageSubpath = separatorIndex === -1 ?
'' : '.' + StringPrototypeSlice(specifier, separatorIndex);
const packageSubpath = '.' + (separatorIndex === -1 ? '' :
StringPrototypeSlice(specifier, separatorIndex));
// ResolveSelf
const packageConfig = getPackageScopeConfig(base, base);
if (packageConfig.exists) {
const packageJSONUrl = pathToFileURL(packageConfig.pjsonPath);
if (packageConfig.name === packageName &&
packageConfig.exports !== undefined) {
if (packageSubpath === './') {
return new URL('./', packageJSONUrl);
} else if (packageSubpath === '') {
return packageMainResolve(packageJSONUrl, packageConfig, base,
conditions);
}
packageConfig.exports !== undefined && packageConfig.exports !== null) {
return packageExportsResolve(
packageJSONUrl, packageSubpath, packageConfig, base, conditions);
packageJSONUrl, packageSubpath, packageConfig, base, conditions
).resolved;
}
}
@@ -642,7 +591,8 @@ function packageResolve(specifier, base, conditions) {
let packageJSONPath = fileURLToPath(packageJSONUrl);
let lastPath;
do {
const stat = tryStatSync(removePackageJsonFromPath(packageJSONPath));
const stat = tryStatSync(StringPrototypeSlice(packageJSONPath, 0,
packageJSONPath.length - 13));
if (!stat.isDirectory()) {
lastPath = packageJSONPath;
packageJSONUrl = new URL((isScoped ?
@@ -654,17 +604,13 @@ function packageResolve(specifier, base, conditions) {
// Package match.
const packageConfig = getPackageConfig(packageJSONPath, base);
if (packageSubpath === './') {
return new URL('./', packageJSONUrl);
} else if (packageSubpath === '') {
return packageMainResolve(packageJSONUrl, packageConfig, base,
conditions);
} else if (packageConfig.exports !== undefined) {
if (packageConfig.exports !== undefined && packageConfig.exports !== null)
return packageExportsResolve(
packageJSONUrl, packageSubpath, packageConfig, base, conditions);
}
return finalizeResolution(
new URL(packageSubpath, packageJSONUrl), base);
packageJSONUrl, packageSubpath, packageConfig, base, conditions
).resolved;
if (packageSubpath === '.')
return legacyMainResolve(packageJSONUrl, packageConfig, base);
return new URL(packageSubpath, packageJSONUrl);
// Cross-platform root check.
} while (packageJSONPath.length !== lastPath.length);
@@ -706,12 +652,12 @@ function moduleResolve(specifier, base, conditions) {
if (shouldBeTreatedAsRelativeOrAbsolutePath(specifier)) {
resolved = new URL(specifier, base);
} else if (specifier[0] === '#') {
resolved = packageInternalResolve(specifier, base, conditions);
({ resolved } = packageImportsResolve(specifier, base, conditions));
} else {
try {
resolved = new URL(specifier);
} catch {
return packageResolve(specifier, base, conditions);
resolved = packageResolve(specifier, base, conditions);
}
}
return finalizeResolution(resolved, base);
@@ -847,5 +793,6 @@ module.exports = {
defaultResolve,
encodedSepRegEx,
getPackageType,
packageInternalResolve
packageExportsResolve,
packageImportsResolve
};

View File

@@ -1,5 +1,6 @@
import { mustCall } from '../common/index.mjs';
import { ok, deepStrictEqual, strictEqual } from 'assert';
import { sep } from 'path';
import { requireFixture, importFixture } from '../fixtures/pkgexports.mjs';
import fromInside from '../fixtures/node_modules/pkgexports/lib/hole.js';
@@ -135,9 +136,9 @@ import fromInside from '../fixtures/node_modules/pkgexports/lib/hole.js';
const notFoundExports = new Map([
// Non-existing file
['pkgexports/sub/not-a-file.js', 'pkgexports/sub/not-a-file.js'],
['pkgexports/sub/not-a-file.js', `pkgexports${sep}not-a-file.js`],
// No extension lookups
['pkgexports/no-ext', 'pkgexports/no-ext'],
['pkgexports/no-ext', `pkgexports${sep}asdf`],
]);
if (!isRequire) {
@@ -153,10 +154,8 @@ import fromInside from '../fixtures/node_modules/pkgexports/lib/hole.js';
for (const [specifier, request] of notFoundExports) {
loadFixture(specifier).catch(mustCall((err) => {
strictEqual(err.code, (isRequire ? '' : 'ERR_') + 'MODULE_NOT_FOUND');
// ESM returns a full file path
assertStartsWith(err.message, isRequire ?
`Cannot find module '${request}'` :
'Cannot find module');
assertIncludes(err.message, request);
assertStartsWith(err.message, 'Cannot find module');
}));
}

View File

@@ -21,7 +21,7 @@ child.on('close', mustCall((code, signal) => {
stderr.includes(
[
'[ERR_INVALID_PACKAGE_CONFIG]: ',
`Invalid package config ${invalidJson}, `,
`Invalid package config ${invalidJson}. `,
`Unexpected token } in JSON at position ${isWindows ? 16 : 14}`
].join(''),
),

View File

@@ -6,7 +6,6 @@ Error [ERR_MODULE_NOT_FOUND]: Cannot find module '*test*fixtures*node_modules*so
Did you mean to import some_module/obj.js?
at new NodeError (internal/errors.js:*:*)
at finalizeResolution (internal/modules/esm/resolve.js:*:*)
at packageResolve (internal/modules/esm/resolve.js:*:*)
at moduleResolve (internal/modules/esm/resolve.js:*:*)
at Loader.defaultResolve [as _resolve] (internal/modules/esm/resolve.js:*:*)
at Loader.resolve (internal/modules/esm/loader.js:*:*)