Skip to content

Commit

Permalink
module: package "exports" error refinements
Browse files Browse the repository at this point in the history
PR-URL: nodejs#31625
Reviewed-By: Jan Krems <jan.krems@gmail.com>
  • Loading branch information
guybedford committed Apr 1, 2020
1 parent d577190 commit 03a28dd
Show file tree
Hide file tree
Showing 10 changed files with 333 additions and 218 deletions.
20 changes: 20 additions & 0 deletions doc/api/errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -1301,6 +1301,12 @@ An invalid HTTP token was supplied.

An IP address is not valid.

<a id="ERR_INVALID_MODULE_SPECIFIER"></a>
### `ERR_INVALID_MODULE_SPECIFIER`

The imported module string is an invalid URL, package name, or package subpath
specifier.

<a id="ERR_INVALID_OPT_VALUE"></a>
### `ERR_INVALID_OPT_VALUE`

Expand All @@ -1316,6 +1322,12 @@ An invalid or unknown file encoding was passed.

An invalid `package.json` file was found which failed parsing.

<a id="ERR_INVALID_PACKAGE_TARGET"></a>
### `ERR_INVALID_PACKAGE_TARGET`

The `package.json` [exports][] field contains an invalid target mapping value
for the attempted module resolution.

<a id="ERR_INVALID_PERFORMANCE_MARK"></a>
### `ERR_INVALID_PERFORMANCE_MARK`

Expand Down Expand Up @@ -1616,6 +1628,13 @@ A non-context-aware native addon was loaded in a process that disallows them.

A given value is out of the accepted range.

<a id="ERR_PACKAGE_PATH_NOT_EXPORTED"></a>
### `ERR_PACKAGE_PATH_NOT_EXPORTED`

The `package.json` [exports][] field does not export the requested subpath.
Because exports are encapsulated, private internal modules that are not exported
cannot be imported through the package resolution, unless using an absolute URL.

<a id="ERR_REQUIRE_ESM"></a>
### `ERR_REQUIRE_ESM`

Expand Down Expand Up @@ -2457,6 +2476,7 @@ such as `process.stdout.on('data')`.
[crypto digest algorithm]: crypto.html#crypto_crypto_gethashes
[domains]: domain.html
[event emitter-based]: events.html#events_class_eventemitter
[exports]: esm.html#esm_package_exports
[file descriptors]: https://en.wikipedia.org/wiki/File_descriptor
[policy]: policy.html
[stream-based]: stream.html
Expand Down
78 changes: 46 additions & 32 deletions doc/api/esm.md
Original file line number Diff line number Diff line change
Expand Up @@ -1424,6 +1424,17 @@ of these top-level routines unless stated otherwise.
_defaultEnv_ is the conditional environment name priority array,
`["node", "import"]`.
The resolver can throw the following errors:
* _Invalid Module Specifier_: Module specifier is an invalid URL, package name
or package subpath specifier.
* _Invalid Package Configuration_: package.json configuration is invalid or
contains an invalid configuration.
* _Invalid Package Target_: Package exports define a target module within the
package that is an invalid type or string target.
* _Package Path Not Exported_: Package exports do not define or permit a target
subpath in the package for the given module.
* _Module Not Found_: The package or module requested does not exist.
<details>
<summary>Resolver algorithm specification</summary>
Expand All @@ -1434,7 +1445,7 @@ _defaultEnv_ is the conditional environment name priority array,
> 1. Set _resolvedURL_ to the result of parsing and reserializing
> _specifier_ as a URL.
> 1. Otherwise, if _specifier_ starts with _"/"_, then
> 1. Throw an _Invalid Specifier_ error.
> 1. Throw an _Invalid Module Specifier_ error.
> 1. Otherwise, if _specifier_ starts with _"./"_ or _"../"_, then
> 1. Set _resolvedURL_ to the URL resolution of _specifier_ relative to
> _parentURL_.
Expand All @@ -1444,8 +1455,9 @@ _defaultEnv_ is the conditional environment name priority array,
> **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 an _Invalid Module Specifier_ error.
> 1. If _resolvedURL_ does not end with a trailing _"/"_ and the file at
> _resolvedURL_ 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_).
Expand All @@ -1456,22 +1468,22 @@ _defaultEnv_ is the conditional environment name priority array,
> 1. Let _packageName_ be *undefined*.
> 1. Let _packageSubpath_ be *undefined*.
> 1. If _packageSpecifier_ is an empty string, then
> 1. Throw an _Invalid Specifier_ error.
> 1. Throw an _Invalid Module Specifier_ error.
> 1. Otherwise,
> 1. If _packageSpecifier_ does not contain a _"/"_ separator, then
> 1. Throw an _Invalid Specifier_ error.
> 1. Throw an _Invalid Module Specifier_ error.
> 1. Set _packageName_ to the substring of _packageSpecifier_
> until the second _"/"_ separator or the end of the string.
> 1. If _packageName_ starts with _"."_ or contains _"\\"_ or _"%"_, then
> 1. Throw an _Invalid Specifier_ error.
> 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
> _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 Specifier_ error.
> 1. Throw an _Invalid Module Specifier_ error.
> 1. Set _selfUrl_ to the result of
> **SELF_REFERENCE_RESOLVE**(_packageName_, _packageSubpath_, _parentURL_).
> 1. If _selfUrl_ isn't empty, return _selfUrl_.
Expand Down Expand Up @@ -1524,7 +1536,7 @@ _defaultEnv_ is the conditional environment name priority array,
> 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.
> 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. Return **PACKAGE_EXPORTS_TARGET_RESOLVE**(_packageURL_,
Expand All @@ -1533,6 +1545,7 @@ _defaultEnv_ is the conditional environment name priority array,
> 1. Let _mainExport_ be the _"."_ property in _pjson.exports_.
> 1. Return **PACKAGE_EXPORTS_TARGET_RESOLVE**(_packageURL_,
> _mainExport_, _""_).
> 1. Throw a _Package Path Not Exported_ error.
> 1. If _pjson.main_ is a String, then
> 1. Let _resolvedMain_ be the URL resolution of _packageURL_, "/", and
> _pjson.main_.
Expand All @@ -1547,7 +1560,7 @@ _defaultEnv_ is the conditional environment name priority array,
**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.
> 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
Expand All @@ -1563,43 +1576,44 @@ _defaultEnv_ is the conditional environment name priority array,
> of the length of _directory_.
> 1. Return **PACKAGE_EXPORTS_TARGET_RESOLVE**(_packageURL_, _target_,
> _subpath_, _defaultEnv_).
> 1. Throw a _Module Not Found_ error.
> 1. Throw a _Package Path Not Exported_ error.
**PACKAGE_EXPORTS_TARGET_RESOLVE**(_packageURL_, _target_, _subpath_, _env_)
> 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.If _target_ is a String, then
> 1. If _target_ does not start with _"./"_ or contains any _"node_modules"_
> segments including _"node_modules"_ percent-encoding, throw an
> _Invalid Package Target_ 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. If _resolvedTarget_ is not contained in _packageURL_, 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. 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 _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_EXPORTS_TARGET_RESOLVE**
> (_packageURL_, _targetValue_, _subpath_, _env_).
> 1. Assert: _resolved_ is a String.
> 1. Return _resolved_.
> 1. Return the result of **PACKAGE_EXPORTS_TARGET_RESOLVE**(
> _packageURL_, _targetValue_, _subpath_, _env_), continuing the
> loop on any _Package Path Not Exported_ error.
> 1. Throw a _Package Path Not Exported_ error.
> 1. Otherwise, if _target_ is an Array, then
> 1. If _target.length is zero, throw an _Invalid Package Target_ error.
> 1. For each item _targetValue_ in _target_, do
> 1. If _targetValue_ is an Array, continue the loop.
> 1. Let _resolved_ be the result of
> **PACKAGE_EXPORTS_TARGET_RESOLVE**(_packageURL_, _targetValue_,
> _subpath_, _env_), continuing the loop on abrupt completion.
> 1. Assert: _resolved_ is a String.
> 1. Return _resolved_.
> 1. Throw a _Module Not Found_ error.
> 1. Return the result of **PACKAGE_EXPORTS_TARGET_RESOLVE**(_packageURL_,
> _targetValue_, _subpath_, _env_), continuing the loop on any
> _Package Path Not Exported_ or _Invalid Package Target_ error.
> 1. Throw the last fallback resolution error.
> 1. Otherwise throw an _Invalid Package Target_ error.
**ESM_FORMAT**(_url_)
Expand Down
29 changes: 28 additions & 1 deletion lib/internal/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,20 @@
const {
ArrayIsArray,
Error,
JSONStringify,
Map,
MathAbs,
NumberIsInteger,
ObjectDefineProperty,
ObjectKeys,
StringPrototypeSlice,
Symbol,
SymbolFor,
WeakMap,
} = primordials;

const sep = process.platform === 'win32' ? '\\' : '/';

const messages = new Map();
const codes = {};

Expand Down Expand Up @@ -1065,14 +1069,29 @@ E('ERR_INVALID_FILE_URL_PATH', 'File URL path %s', TypeError);
E('ERR_INVALID_HANDLE_TYPE', 'This handle type cannot be sent', TypeError);
E('ERR_INVALID_HTTP_TOKEN', '%s must be a valid HTTP token ["%s"]', TypeError);
E('ERR_INVALID_IP_ADDRESS', 'Invalid IP address: %s', TypeError);
E('ERR_INVALID_MODULE_SPECIFIER', (pkgPath, subpath) => {
assert(subpath !== '.');
return `Package subpath '${subpath}' is not a valid module request for the ` +
`"exports" resolution of ${pkgPath}${sep}package.json`;
}, TypeError);
E('ERR_INVALID_OPT_VALUE', (name, value) =>
`The value "${String(value)}" is invalid for option "${name}"`,
TypeError,
RangeError);
E('ERR_INVALID_OPT_VALUE_ENCODING',
'The value "%s" is invalid for option "encoding"', TypeError);
E('ERR_INVALID_PACKAGE_CONFIG',
'Invalid package config for \'%s\', %s', Error);
`Invalid package config %s${sep}package.json, %s`, Error);
E('ERR_INVALID_PACKAGE_TARGET', (pkgPath, key, subpath, target) => {
if (key === '.') {
return `Invalid "exports" main target ${JSONStringify(target)} defined ` +
`in the package config ${pkgPath}${sep}package.json`;
} else {
return `Invalid "exports" target ${JSONStringify(target)} defined for '${
StringPrototypeSlice(key, 0, -subpath.length || key.length)}' in the ` +
`package config ${pkgPath}${sep}package.json`;
}
}, Error);
E('ERR_INVALID_PERFORMANCE_MARK',
'The "%s" performance mark has not been set', Error);
E('ERR_INVALID_PROTOCOL',
Expand Down Expand Up @@ -1216,6 +1235,14 @@ E('ERR_OUT_OF_RANGE',
msg += ` It must be ${range}. Received ${received}`;
return msg;
}, RangeError);
E('ERR_PACKAGE_PATH_NOT_EXPORTED', (pkgPath, subpath) => {
if (subpath === '.') {
return `No "exports" main resolved in ${pkgPath}${sep}package.json`;
} else {
return `Package subpath '${subpath}' is not defined by "exports" in ${
pkgPath}${sep}package.json`;
}
}, Error);
E('ERR_REQUIRE_ESM',
(filename, parentPath = null, packageJsonPath = null) => {
let msg = `Must use import to load ES Module: ${filename}`;
Expand Down
Loading

0 comments on commit 03a28dd

Please sign in to comment.