diff --git a/doc/api/errors.md b/doc/api/errors.md
index 74a4faa88b929f..d231cf28857dc0 100644
--- a/doc/api/errors.md
+++ b/doc/api/errors.md
@@ -1319,6 +1319,12 @@ An invalid HTTP token was supplied.
An IP address is not valid.
+
+### `ERR_INVALID_MODULE_SPECIFIER`
+
+The imported module string is an invalid URL, package name, or package subpath
+specifier.
+
### `ERR_INVALID_OPT_VALUE`
@@ -1334,6 +1340,12 @@ An invalid or unknown file encoding was passed.
An invalid `package.json` file was found which failed parsing.
+
+### `ERR_INVALID_PACKAGE_TARGET`
+
+The `package.json` [exports][] field contains an invalid target mapping value
+for the attempted module resolution.
+
### `ERR_INVALID_PERFORMANCE_MARK`
@@ -1640,6 +1652,13 @@ A non-context-aware native addon was loaded in a process that disallows them.
A given value is out of the accepted range.
+
+### `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.
+
### `ERR_REQUIRE_ESM`
@@ -2499,6 +2518,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
diff --git a/doc/api/esm.md b/doc/api/esm.md
index f1378b59ad10a2..7abaab87e0f6f5 100644
--- a/doc/api/esm.md
+++ b/doc/api/esm.md
@@ -1391,6 +1391,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.
+
Resolver algorithm specification
@@ -1401,7 +1412,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_.
@@ -1411,7 +1422,7 @@ _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. 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.
@@ -1425,14 +1436,14 @@ _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
@@ -1440,7 +1451,7 @@ _defaultEnv_ is the conditional environment name priority array,
> _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_.
@@ -1497,7 +1508,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_,
@@ -1506,6 +1517,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_.
@@ -1520,7 +1532,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
@@ -1536,43 +1548,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_)
diff --git a/lib/internal/errors.js b/lib/internal/errors.js
index 319c836695e88f..8c18eabb7a8d06 100644
--- a/lib/internal/errors.js
+++ b/lib/internal/errors.js
@@ -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 = {};
@@ -1069,6 +1073,11 @@ 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,
@@ -1076,7 +1085,17 @@ E('ERR_INVALID_OPT_VALUE', (name, value) =>
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',
@@ -1221,6 +1240,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}`;
diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js
index a1b3270b0919ec..c2a8b2a3f7435b 100644
--- a/lib/internal/modules/cjs/loader.js
+++ b/lib/internal/modules/cjs/loader.js
@@ -82,6 +82,9 @@ 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');
@@ -498,13 +501,9 @@ function applyExports(basePath, expansion) {
if (ObjectPrototypeHasOwnProperty(pkgExports, mappingKey)) {
const mapping = pkgExports[mappingKey];
return resolveExportsTarget(pathToFileURL(basePath + '/'), mapping, '',
- basePath, mappingKey);
+ mappingKey);
}
- // Fallback to CJS main lookup when no main export is defined
- if (mappingKey === '.')
- return basePath;
-
let dirMatch = '';
for (const candidateKey of ObjectKeys(pkgExports)) {
if (candidateKey[candidateKey.length - 1] !== '/') continue;
@@ -518,18 +517,11 @@ function applyExports(basePath, expansion) {
const mapping = pkgExports[dirMatch];
const subpath = StringPrototypeSlice(mappingKey, dirMatch.length);
return resolveExportsTarget(pathToFileURL(basePath + '/'), mapping,
- subpath, basePath, mappingKey);
+ subpath, mappingKey);
}
}
- // Fallback to CJS main lookup when no main export is defined
- if (mappingKey === '.')
- return basePath;
- // eslint-disable-next-line no-restricted-syntax
- const e = new Error(`Package exports for '${basePath}' do not define ` +
- `a '${mappingKey}' subpath`);
- e.code = 'MODULE_NOT_FOUND';
- throw e;
+ throw new ERR_PACKAGE_PATH_NOT_EXPORTED(basePath, mappingKey);
}
// This only applies to requests of a specific form:
@@ -564,39 +556,53 @@ function isArrayIndex(p) {
return n >= 0 && n < (2 ** 32) - 1;
}
-function resolveExportsTarget(pkgPath, target, subpath, basePath, mappingKey) {
+function resolveExportsTarget(baseUrl, target, subpath, mappingKey) {
if (typeof target === 'string') {
- if (target.startsWith('./') &&
- (subpath.length === 0 || target.endsWith('/'))) {
- const resolvedTarget = new URL(target, pkgPath);
- const pkgPathPath = pkgPath.pathname;
- const resolvedTargetPath = resolvedTarget.pathname;
- if (StringPrototypeStartsWith(resolvedTargetPath, pkgPathPath) &&
+ 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) {
- const resolved = new URL(subpath, resolvedTarget);
- const resolvedPath = resolved.pathname;
- if (StringPrototypeStartsWith(resolvedPath, resolvedTargetPath) &&
- StringPrototypeIndexOf(resolvedPath, '/node_modules/',
- pkgPathPath.length - 1) === -1) {
- return fileURLToPath(resolved);
- }
- }
+ 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(StringPrototypeSlice(baseUrl.pathname
+ , 0, -1), mappingKey, subpath, target);
+ const resolved = new URL(subpath, resolvedTarget);
+ const resolvedPath = resolved.pathname;
+ if (StringPrototypeStartsWith(resolvedPath, resolvedTargetPath) &&
+ StringPrototypeIndexOf(resolvedPath, '/node_modules/',
+ pkgPathPath.length - 1) === -1) {
+ return fileURLToPath(resolved);
+ }
+ throw new ERR_INVALID_MODULE_SPECIFIER(StringPrototypeSlice(baseUrl.pathname
+ , 0, -1), mappingKey);
} else if (ArrayIsArray(target)) {
+ if (target.length === 0)
+ throw new ERR_INVALID_PACKAGE_TARGET(StringPrototypeSlice(baseUrl.pathname
+ , 0, -1), mappingKey, subpath, target);
for (const targetValue of target) {
- if (ArrayIsArray(targetValue)) continue;
try {
- return resolveExportsTarget(pkgPath, targetValue, subpath, basePath,
- mappingKey);
+ return resolveExportsTarget(baseUrl, targetValue, subpath, mappingKey);
} catch (e) {
- if (e.code !== 'MODULE_NOT_FOUND') throw e;
+ if (e.code !== 'ERR_PACKAGE_PATH_NOT_EXPORTED' &&
+ e.code !== 'ERR_INVALID_PACKAGE_TARGET')
+ throw e;
}
}
+ // Throw last fallback error
+ resolveExportsTarget(baseUrl, target[target.length - 1], subpath,
+ mappingKey);
+ assert(false);
} else if (typeof target === 'object' && target !== null) {
const keys = ObjectKeys(target);
if (keys.some(isArrayIndex)) {
- throw new ERR_INVALID_PACKAGE_CONFIG(basePath, '"exports" cannot ' +
+ throw new ERR_INVALID_PACKAGE_CONFIG(baseUrl, '"exports" cannot ' +
'contain numeric property keys.');
}
for (const p of keys) {
@@ -605,34 +611,26 @@ function resolveExportsTarget(pkgPath, target, subpath, basePath, mappingKey) {
case 'require':
try {
emitExperimentalWarning('Conditional exports');
- const result = resolveExportsTarget(pkgPath, target[p], subpath,
- basePath, mappingKey);
- return result;
+ return resolveExportsTarget(baseUrl, target[p], subpath,
+ mappingKey);
} catch (e) {
- if (e.code !== 'MODULE_NOT_FOUND') throw e;
+ if (e.code !== 'ERR_PACKAGE_PATH_NOT_EXPORTED') throw e;
}
break;
case 'default':
try {
- return resolveExportsTarget(pkgPath, target.default, subpath,
- basePath, mappingKey);
+ return resolveExportsTarget(baseUrl, target.default, subpath,
+ mappingKey);
} catch (e) {
- if (e.code !== 'MODULE_NOT_FOUND') throw e;
+ if (e.code !== 'ERR_PACKAGE_PATH_NOT_EXPORTED') throw e;
}
}
}
+ throw new ERR_PACKAGE_PATH_NOT_EXPORTED(
+ StringPrototypeSlice(baseUrl.pathname, 0, -1), mappingKey + subpath);
}
- let e;
- if (mappingKey !== '.') {
- // eslint-disable-next-line no-restricted-syntax
- e = new Error(`Package exports for '${basePath}' do not define a ` +
- `valid '${mappingKey}' target${subpath ? ' for ' + subpath : ''}`);
- } else {
- // eslint-disable-next-line no-restricted-syntax
- e = new Error(`No valid exports main found for '${basePath}'`);
- }
- e.code = 'MODULE_NOT_FOUND';
- throw e;
+ throw new ERR_INVALID_PACKAGE_TARGET(
+ StringPrototypeSlice(baseUrl.pathname, 0, -1), mappingKey, subpath, target);
}
Module._findPath = function(request, paths, isMain) {
diff --git a/src/module_wrap.cc b/src/module_wrap.cc
index 68359178f4ab38..436a6e98e73fe5 100644
--- a/src/module_wrap.cc
+++ b/src/module_wrap.cc
@@ -858,10 +858,20 @@ void ThrowExportsNotFound(Environment* env,
const std::string& subpath,
const URL& pjson_url,
const URL& base) {
- const std::string msg = "Package exports for " +
- pjson_url.ToFilePath() + " do not define a '" + subpath +
- "' subpath, imported from " + base.ToFilePath();
- node::THROW_ERR_MODULE_NOT_FOUND(env, msg.c_str());
+ const std::string msg = "Package subpath '" + subpath + "' is not defined" +
+ " by \"exports\" in " + pjson_url.ToFilePath() + " imported from " +
+ base.ToFilePath();
+ node::THROW_ERR_PACKAGE_PATH_NOT_EXPORTED(env, msg.c_str());
+}
+
+void ThrowSubpathInvalid(Environment* env,
+ const std::string& subpath,
+ const URL& pjson_url,
+ const URL& base) {
+ const std::string msg = "Package subpath '" + subpath + "' is not a valid " +
+ "module request for the \"exports\" resolution of " +
+ pjson_url.ToFilePath() + " imported from " + base.ToFilePath();
+ node::THROW_ERR_INVALID_MODULE_SPECIFIER(env, msg.c_str());
}
void ThrowExportsInvalid(Environment* env,
@@ -870,14 +880,15 @@ void ThrowExportsInvalid(Environment* env,
const URL& pjson_url,
const URL& base) {
if (subpath.length()) {
- const std::string msg = "Cannot resolve package exports target '" + target +
- "' matched for '" + subpath + "' in " + pjson_url.ToFilePath() +
- ", imported from " + base.ToFilePath();
- node::THROW_ERR_MODULE_NOT_FOUND(env, msg.c_str());
+ const std::string msg = "Invalid \"exports\" target \"" + target +
+ "\" defined for '" + subpath + "' in the package config " +
+ pjson_url.ToFilePath() + " imported from " + base.ToFilePath();
+ node::THROW_ERR_INVALID_PACKAGE_TARGET(env, msg.c_str());
} else {
- const std::string msg = "Cannot resolve package main '" + target + "' in" +
- pjson_url.ToFilePath() + ", imported from " + base.ToFilePath();
- node::THROW_ERR_MODULE_NOT_FOUND(env, msg.c_str());
+ const std::string msg = "Invalid \"exports\" main target " + target +
+ " defined in the package config " + pjson_url.ToFilePath() +
+ " imported from " + base.ToFilePath();
+ node::THROW_ERR_INVALID_PACKAGE_TARGET(env, msg.c_str());
}
}
@@ -887,14 +898,20 @@ void ThrowExportsInvalid(Environment* env,
const URL& pjson_url,
const URL& base) {
Local target_string;
- if (target->ToString(env->context()).ToLocal(&target_string)) {
- Utf8Value target_utf8(env->isolate(), target_string);
- std::string target_str(*target_utf8, target_utf8.length());
- if (target->IsArray()) {
- target_str = '[' + target_str + ']';
- }
- ThrowExportsInvalid(env, subpath, target_str, pjson_url, base);
+ if (target->IsObject()) {
+ if (!v8::JSON::Stringify(env->context(), target.As(),
+ v8::String::Empty(env->isolate())).ToLocal(&target_string))
+ return;
+ } else {
+ if (!target->ToString(env->context()).ToLocal(&target_string))
+ return;
+ }
+ Utf8Value target_utf8(env->isolate(), target_string);
+ std::string target_str(*target_utf8, target_utf8.length());
+ if (target->IsArray()) {
+ target_str = '[' + target_str + ']';
}
+ ThrowExportsInvalid(env, subpath, target_str, pjson_url, base);
}
Maybe ResolveExportsTargetString(Environment* env,
@@ -902,18 +919,13 @@ Maybe ResolveExportsTargetString(Environment* env,
const std::string& subpath,
const std::string& match,
const URL& pjson_url,
- const URL& base,
- bool throw_invalid = true) {
+ const URL& base) {
if (target.substr(0, 2) != "./") {
- if (throw_invalid) {
- ThrowExportsInvalid(env, match, target, pjson_url, base);
- }
+ ThrowExportsInvalid(env, match, target, pjson_url, base);
return Nothing();
}
if (subpath.length() > 0 && target.back() != '/') {
- if (throw_invalid) {
- ThrowExportsInvalid(env, match, target, pjson_url, base);
- }
+ ThrowExportsInvalid(env, match, target, pjson_url, base);
return Nothing();
}
URL resolved(target, pjson_url);
@@ -922,9 +934,7 @@ Maybe ResolveExportsTargetString(Environment* env,
if (resolved_path.find(pkg_path) != 0 ||
resolved_path.find("/node_modules/", pkg_path.length() - 1) !=
std::string::npos) {
- if (throw_invalid) {
- ThrowExportsInvalid(env, match, target, pjson_url, base);
- }
+ ThrowExportsInvalid(env, match, target, pjson_url, base);
return Nothing();
}
if (subpath.length() == 0) return Just(resolved);
@@ -933,9 +943,7 @@ Maybe ResolveExportsTargetString(Environment* env,
if (subpath_resolved_path.find(resolved_path) != 0 ||
subpath_resolved_path.find("/node_modules/", pkg_path.length() - 1)
!= std::string::npos) {
- if (throw_invalid) {
- ThrowExportsInvalid(env, match, target + subpath, pjson_url, base);
- }
+ ThrowSubpathInvalid(env, match + subpath, pjson_url, base);
return Nothing();
}
return Just(subpath_resolved);
@@ -965,15 +973,14 @@ Maybe ResolveExportsTarget(Environment* env,
Local target,
const std::string& subpath,
const std::string& pkg_subpath,
- const URL& base,
- bool throw_invalid = true) {
+ const URL& base) {
Isolate* isolate = env->isolate();
Local context = env->context();
if (target->IsString()) {
Utf8Value target_utf8(isolate, target.As());
std::string target_str(*target_utf8, target_utf8.length());
Maybe resolved = ResolveExportsTargetString(env, target_str, subpath,
- pkg_subpath, pjson_url, base, throw_invalid);
+ pkg_subpath, pjson_url, base);
if (resolved.IsNothing()) {
return Nothing();
}
@@ -982,40 +989,56 @@ Maybe ResolveExportsTarget(Environment* env,
Local target_arr = target.As();
const uint32_t length = target_arr->Length();
if (length == 0) {
- if (throw_invalid) {
- ThrowExportsInvalid(env, pkg_subpath, target, pjson_url, base);
- }
+ ThrowExportsInvalid(env, pkg_subpath, target, pjson_url, base);
return Nothing();
}
for (uint32_t i = 0; i < length; i++) {
auto target_item = target_arr->Get(context, i).ToLocalChecked();
- if (!target_item->IsArray()) {
+ {
+ TryCatchScope try_catch(env);
Maybe resolved = ResolveExportsTarget(env, pjson_url,
- target_item, subpath, pkg_subpath, base, false);
- if (resolved.IsNothing()) continue;
+ target_item, subpath, pkg_subpath, base);
+ if (resolved.IsNothing()) {
+ CHECK(try_catch.HasCaught());
+ if (try_catch.Exception().IsEmpty()) return Nothing();
+ Local