diff --git a/doc/api/cli.md b/doc/api/cli.md
index fa46a00aff8f0f..d305644a0ff274 100644
--- a/doc/api/cli.md
+++ b/doc/api/cli.md
@@ -170,6 +170,15 @@ the ability to import a directory that has an index file.
Please see [customizing esm specifier resolution][] for example usage.
+### `--experimental-conditional-exports
+
+
+Enable experimental support for the `"require"` and `"node"` conditional
+package export resolutions.
+See [Conditional Exports][] for more information.
+
### `--experimental-json-modules`
```js
-// ./node_modules/es-module-package/package.json
{
"exports": {
- ".": "./main.js"
+ "./submodule": ["not:valid", "./submodule.js"]
}
}
```
-where the "." indicates loading the package without any subpath. Exports will
-always override any existing `"main"` value for both CommonJS and
-ES module packages.
+Since `"not:valid"` is not a supported target, `"./submodule.js"` is used
+instead as the fallback, as if it were the only target.
+
+Defining a `"."` export will define the main entry point for the package,
+and will always take precedence over the `"main"` field in the `package.json`.
-For packages with only a main entry point, an `"exports"` value of just
-a string is also supported:
+This allows defining a different entry point for Node.js versions that support
+ECMAScript modules and versions that don't, for example:
+
+
+```js
+{
+ "main": "./main-legacy.cjs",
+ "exports": {
+ ".": "./main-modern.cjs"
+ }
+}
+```
+
+#### Conditional Exports
+
+Conditional exports provide a way to map to different paths depending on
+certain conditions. They are supported for both CommonJS and ES module imports.
+
+For example, a package that wants to provide different ES module exports for
+Node.js and the browser can be written:
+
+
+```js
+// ./node_modules/pkg/package.json
+{
+ "type": "module",
+ "main": "./index.js",
+ "exports": {
+ "./feature": {
+ "browser": "./feature-browser.js",
+ "default": "./feature-default.js"
+ }
+ }
+}
+```
+
+When resolving the `"."` export, if no matching target is found, the `"main"`
+will be used as the final fallback.
+
+The conditions supported in Node.js are matched in the following order:
+
+1. `"require"` - matched when the package is loaded via `require()`.
+ _This is currently only supported behind the
+ `--experimental-conditional-exports` flag._
+2. `"node"` - matched for any Node.js environment. Can be a CommonJS or ES
+ module file. _This is currently only supported behind the
+ `--experimental-conditional-exports` flag._
+3. `"default"` - the generic fallback that will always match if no other
+ more specific condition is matched first. Can be a CommonJS or ES module
+ file.
+
+Using the `"require"` condition it is possible to define a package that will
+have a different exported value for CommonJS and ES modules, which can be a
+hazard in that it can result in having two separate instances of the same
+package in use in an application, which can cause a number of bugs.
+
+Other conditions such as `"browser"`, `"electron"`, `"deno"`, `"react-native"`,
+etc. could be defined in other runtimes or tools.
+
+#### Exports Sugar
+
+If the `"."` export is the only export, the `"exports"` field provides sugar
+for this case being the direct `"exports"` field value.
+
+If the `"."` export has a fallback array or string value, then the `"exports"`
+field can be set to this value directly.
+
+
+```js
+{
+ "exports": {
+ ".": "./main.js"
+ }
+}
+```
+
+can be written:
```js
-// ./node_modules/es-module-package/package.json
{
"exports": "./main.js"
}
```
-Any invalid exports entries will be ignored. This includes exports not
-starting with `"./"` or a missing trailing `"/"` for directory exports.
+When using conditional exports, the rule is that all keys in the object mapping
+must not start with a `"."` otherwise they would be indistinguishable from
+exports subpaths.
-Array fallback support is provided for exports, similarly to import maps
-in order to be forward-compatible with fallback workflows in future:
+
+```js
+{
+ "exports": {
+ ".": {
+ "require": "./main.cjs",
+ "default": "./main.js"
+ }
+ }
+}
+```
+
+can be written:
```js
{
"exports": {
- "./submodule": ["not:valid", "./submodule.js"]
+ "require": "./main.cjs",
+ "default": "./main.js"
}
}
```
-Since `"not:valid"` is not a supported target, `"./submodule.js"` is used
-instead as the fallback, as if it were the only target.
+If writing any exports value that mixes up these two forms, an error will be
+thrown:
+
+
+```js
+{
+ // Throws on resolution!
+ "exports": {
+ "./feature": "./lib/feature.js",
+ "require": "./main.cjs",
+ "default": "./main.js"
+ }
+}
+```
## import Specifiers
@@ -806,6 +913,9 @@ of these top-level routines unless stated otherwise.
_isMain_ is **true** when resolving the Node.js application entry point.
+_defaultEnv_ is the conditional environment name priority array,
+`["node", "default"]`.
+
Resolver algorithm specification
@@ -905,14 +1015,16 @@ _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.exports_ is not **null** or **undefined**, then
-> 1. If _pjson.exports_ is a String or Array, then
+> 1. If _exports_ is an Object with both a key starting with _"."_ and a key
+> not starting with _"."_, throw a "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_,
+> _pjson.exports_, _""_).
+> 1. If _pjson.exports_ is an Object containing a _"."_ property, then
+> 1. Let _mainExport_ be the _"."_ property in _pjson.exports_.
> 1. Return **PACKAGE_EXPORTS_TARGET_RESOLVE**(_packageURL_,
-> _pjson.exports_, "")_.
-> 1. If _pjson.exports is an Object, then
-> 1. If _pjson.exports_ contains a _"."_ property, then
-> 1. Let _mainExport_ be the _"."_ property in _pjson.exports_.
-> 1. Return **PACKAGE_EXPORTS_TARGET_RESOLVE**(_packageURL_,
-> _mainExport_, "")_.
+> _mainExport_, _""_).
> 1. If _pjson.main_ is a String, then
> 1. Let _resolvedMain_ be the URL resolution of _packageURL_, "/", and
> _pjson.main_.
@@ -926,13 +1038,14 @@ _isMain_ is **true** when resolving the Node.js application entry point.
> 1. Return _legacyMainURL_.
**PACKAGE_EXPORTS_RESOLVE**(_packageURL_, _packagePath_, _exports_)
-
-> 1. If _exports_ is an Object, 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 _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_EXPORTS_TARGET_RESOLVE**(_packageURL_, _target_,
-> _""_).
+> _""_, _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
@@ -941,10 +1054,10 @@ _isMain_ is **true** when resolving the Node.js application entry point.
> 1. Let _subpath_ be the substring of _target_ starting at the index
> of the length of _directory_.
> 1. Return **PACKAGE_EXPORTS_TARGET_RESOLVE**(_packageURL_, _target_,
-> _subpath_).
+> _subpath_, _defaultEnv_).
> 1. Throw a _Module Not Found_ error.
-**PACKAGE_EXPORTS_TARGET_RESOLVE**(_packageURL_, _target_, _subpath_)
+**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_
@@ -960,12 +1073,20 @@ _isMain_ is **true** when resolving the Node.js application entry point.
> _subpath_ and _resolvedTarget_.
> 1. If _resolved_ is contained in _resolvedTarget_, then
> 1. Return _resolved_.
+> 1. Otherwise, if _target_ is a non-null Object, then
+> 1. If _target_ has an object key matching one of the names in _env_, then
+> 1. Let _targetValue_ be the corresponding value of the first object key
+> of _target_ in _env_.
+> 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. 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. If _targetValue_ is an Array, continue the loop.
> 1. Let _resolved_ be the result of
> **PACKAGE_EXPORTS_TARGET_RESOLVE**(_packageURL_, _targetValue_,
-> _subpath_), continuing the loop on abrupt completion.
+> _subpath_, _env_), continuing the loop on abrupt completion.
> 1. Assert: _resolved_ is a String.
> 1. Return _resolved_.
> 1. Throw a _Module Not Found_ error.
@@ -1033,6 +1154,7 @@ success!
```
[CommonJS]: modules.html
+[Conditional Exports]: #esm_conditional_exports
[ECMAScript-modules implementation]: https://github.com/nodejs/modules/blob/master/doc/plan-for-new-modules-implementation.md
[ES Module Integration Proposal for Web Assembly]: https://github.com/webassembly/esm-integration
[Node.js EP for ES Modules]: https://github.com/nodejs/node-eps/blob/master/002-es-modules.md
@@ -1045,7 +1167,7 @@ success!
[`import`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
[`module.createRequire()`]: modules.html#modules_module_createrequire_filename
[`module.syncBuiltinESMExports()`]: modules.html#modules_module_syncbuiltinesmexports
-[dynamic instantiate hook]: #esm_dynamic_instantiate_hook
[package exports]: #esm_package_exports
+[dynamic instantiate hook]: #esm_dynamic_instantiate_hook
[special scheme]: https://url.spec.whatwg.org/#special-scheme
[the official standard format]: https://tc39.github.io/ecma262/#sec-modules
diff --git a/doc/api/modules.md b/doc/api/modules.md
index 8715218b32c8a4..d6856629210be7 100644
--- a/doc/api/modules.md
+++ b/doc/api/modules.md
@@ -232,12 +232,17 @@ RESOLVE_BARE_SPECIFIER(DIR, X)
2. If X matches this pattern and DIR/name/package.json is a file:
a. Parse DIR/name/package.json, and look for "exports" field.
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. let RESOLVED_URL =
+ c. If "exports" is an object with some keys starting with "." and some keys
+ not starting with ".", throw "invalid config".
+ c. If "exports" is a string, or object with no keys starting with ".", treat
+ it as having that value as its "." object property.
+ d. If subpath is "." and "exports" does not have a "." entry, GOTO 3.
+ e. Find the longest key in "exports" that the subpath starts with.
+ f. If no such key can be found, throw "not found".
+ g. 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)
+ h. return fileURLToPath(RESOLVED_URL)
3. return DIR/X
```
diff --git a/doc/node.1 b/doc/node.1
index 30d63b216dfd8b..99ff358df9c419 100644
--- a/doc/node.1
+++ b/doc/node.1
@@ -113,6 +113,9 @@ Requires Node.js to be built with
.It Fl -es-module-specifier-resolution
Select extension resolution algorithm for ES Modules; either 'explicit' (default) or 'node'
.
+.It Fl -experimental-conditional-exports
+Enable experimental support for "require" and "node" conditional export targets.
+.
.It Fl -experimental-json-modules
Enable experimental JSON interop support for the ES Module loader.
.
diff --git a/lib/internal/errors.js b/lib/internal/errors.js
index 2684931a77e299..6d4a582631810c 100644
--- a/lib/internal/errors.js
+++ b/lib/internal/errors.js
@@ -981,7 +981,7 @@ 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 in \'%s\' imported from %s', Error);
+ 'Invalid package config for \'%s\', %s', Error);
E('ERR_INVALID_PERFORMANCE_MARK',
'The "%s" performance mark has not been set', Error);
E('ERR_INVALID_PROTOCOL',
diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js
index 7df91ce4fd1c67..3aee3399b8e904 100644
--- a/lib/internal/modules/cjs/loader.js
+++ b/lib/internal/modules/cjs/loader.js
@@ -59,6 +59,8 @@ const preserveSymlinks = getOptionValue('--preserve-symlinks');
const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main');
const experimentalModules = getOptionValue('--experimental-modules');
const experimentalSelf = getOptionValue('--experimental-resolve-self');
+const experimentalConditionalExports =
+ getOptionValue('--experimental-conditional-exports');
const manifest = getOptionValue('--experimental-policy') ?
require('internal/process/policy').manifest :
null;
@@ -67,6 +69,7 @@ const { compileFunction } = internalBinding('contextify');
const {
ERR_INVALID_ARG_VALUE,
ERR_INVALID_OPT_VALUE,
+ ERR_INVALID_PACKAGE_CONFIG,
ERR_REQUIRE_ESM
} = require('internal/errors').codes;
const { validateString } = require('internal/validators');
@@ -441,7 +444,6 @@ function trySelf(paths, exts, isMain, trailingSlash, request) {
if (expansion) {
// Use exports
const fromExports = applyExports(basePath, expansion);
- if (!fromExports) return false;
return resolveBasePath(fromExports, exts, isMain, trailingSlash, request);
} else {
// Use main field
@@ -449,17 +451,51 @@ function trySelf(paths, exts, isMain, trailingSlash, request) {
}
}
+function isConditionalDotExportSugar(exports, basePath) {
+ if (typeof exports === 'string')
+ return true;
+ if (Array.isArray(exports))
+ return true;
+ if (typeof exports !== 'object')
+ return false;
+ let isConditional = false;
+ let firstCheck = true;
+ for (const key of Object.keys(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 pkgExports = readPackageExports(basePath);
const mappingKey = `.${expansion}`;
- if (typeof pkgExports === 'object' && pkgExports !== null) {
+ let pkgExports = readPackageExports(basePath);
+ if (pkgExports === undefined || pkgExports === null || !experimentalModules)
+ return path.resolve(basePath, mappingKey);
+
+ if (isConditionalDotExportSugar(pkgExports, basePath))
+ pkgExports = { '.': pkgExports };
+
+ if (typeof pkgExports === 'object') {
if (ObjectPrototype.hasOwnProperty(pkgExports, mappingKey)) {
const mapping = pkgExports[mappingKey];
return resolveExportsTarget(pathToFileURL(basePath + '/'), mapping, '',
basePath, mappingKey);
}
+ // Fallback to CJS main lookup when no main export is defined
+ if (mappingKey === '.')
+ return basePath;
+
let dirMatch = '';
for (const candidateKey of Object.keys(pkgExports)) {
if (candidateKey[candidateKey.length - 1] !== '/') continue;
@@ -476,19 +512,15 @@ function applyExports(basePath, expansion) {
subpath, basePath, mappingKey);
}
}
- if (mappingKey === '.' && typeof pkgExports === 'string') {
- return resolveExportsTarget(pathToFileURL(basePath + '/'), pkgExports,
- '', basePath, mappingKey);
- }
- if (pkgExports != null) {
- // 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;
- }
+ // Fallback to CJS main lookup when no main export is defined
+ if (mappingKey === '.')
+ return basePath;
- return path.resolve(basePath, mappingKey);
+ // 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;
}
// This only applies to requests of a specific form:
@@ -532,7 +564,7 @@ function resolveExportsTarget(pkgPath, target, subpath, basePath, mappingKey) {
}
} else if (Array.isArray(target)) {
for (const targetValue of target) {
- if (typeof targetValue !== 'string') continue;
+ if (Array.isArray(targetValue)) continue;
try {
return resolveExportsTarget(pkgPath, targetValue, subpath, basePath,
mappingKey);
@@ -540,10 +572,43 @@ function resolveExportsTarget(pkgPath, target, subpath, basePath, mappingKey) {
if (e.code !== 'MODULE_NOT_FOUND') throw e;
}
}
+ } else if (typeof target === 'object' && target !== null) {
+ if (experimentalConditionalExports &&
+ ObjectPrototype.hasOwnProperty(target, 'require')) {
+ try {
+ return resolveExportsTarget(pkgPath, target.require, subpath,
+ basePath, mappingKey);
+ } catch (e) {
+ if (e.code !== 'MODULE_NOT_FOUND') throw e;
+ }
+ }
+ if (experimentalConditionalExports &&
+ ObjectPrototype.hasOwnProperty(target, 'node')) {
+ try {
+ return resolveExportsTarget(pkgPath, target.node, subpath,
+ basePath, mappingKey);
+ } catch (e) {
+ if (e.code !== 'MODULE_NOT_FOUND') throw e;
+ }
+ }
+ if (ObjectPrototype.hasOwnProperty(target, 'default')) {
+ try {
+ return resolveExportsTarget(pkgPath, target.default, subpath,
+ basePath, mappingKey);
+ } catch (e) {
+ if (e.code !== 'MODULE_NOT_FOUND') throw e;
+ }
+ }
+ }
+ 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}'`);
}
- // eslint-disable-next-line no-restricted-syntax
- const e = new Error(`Package exports for '${basePath}' do not define a ` +
- `valid '${mappingKey}' target${subpath ? 'for ' + subpath : ''}`);
e.code = 'MODULE_NOT_FOUND';
throw e;
}
diff --git a/src/env.h b/src/env.h
index 6113d3adbf06d4..11e3a9f1f9f3b2 100644
--- a/src/env.h
+++ b/src/env.h
@@ -201,6 +201,7 @@ constexpr size_t kFsStatsBufferLength =
V(crypto_rsa_pss_string, "rsa-pss") \
V(cwd_string, "cwd") \
V(data_string, "data") \
+ V(default_string, "default") \
V(dest_string, "dest") \
V(destroyed_string, "destroyed") \
V(detached_string, "detached") \
@@ -215,6 +216,7 @@ constexpr size_t kFsStatsBufferLength =
V(dns_srv_string, "SRV") \
V(dns_txt_string, "TXT") \
V(done_string, "done") \
+ V(dot_string, ".") \
V(duration_string, "duration") \
V(emit_warning_string, "emitWarning") \
V(empty_object_string, "{}") \
@@ -279,6 +281,7 @@ constexpr size_t kFsStatsBufferLength =
V(netmask_string, "netmask") \
V(next_string, "next") \
V(nistcurve_string, "nistCurve") \
+ V(node_string, "node") \
V(nsname_string, "nsname") \
V(ocsp_request_string, "OCSPRequest") \
V(oncertcb_string, "oncertcb") \
diff --git a/src/module_wrap.cc b/src/module_wrap.cc
index 4c4a1ce863849e..5745cce9e099ab 100644
--- a/src/module_wrap.cc
+++ b/src/module_wrap.cc
@@ -835,10 +835,16 @@ void ThrowExportsInvalid(Environment* env,
const std::string& target,
const URL& pjson_url,
const URL& base) {
- 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());
+ 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());
+ } 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());
+ }
}
void ThrowExportsInvalid(Environment* env,
@@ -857,13 +863,13 @@ void ThrowExportsInvalid(Environment* env,
}
}
-Maybe ResolveExportsTarget(Environment* env,
- const std::string& target,
- const std::string& subpath,
- const std::string& match,
- const URL& pjson_url,
- const URL& base,
- bool throw_invalid = true) {
+Maybe ResolveExportsTargetString(Environment* env,
+ const std::string& target,
+ const std::string& subpath,
+ const std::string& match,
+ const URL& pjson_url,
+ const URL& base,
+ bool throw_invalid = true) {
if (target.substr(0, 2) != "./") {
if (throw_invalid) {
ThrowExportsInvalid(env, match, target, pjson_url, base);
@@ -901,68 +907,142 @@ Maybe ResolveExportsTarget(Environment* env,
return Just(subpath_resolved);
}
+Maybe ResolveExportsTarget(Environment* env,
+ const URL& pjson_url,
+ Local target,
+ const std::string& subpath,
+ const std::string& pkg_subpath,
+ const URL& base,
+ bool throw_invalid = true) {
+ 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);
+ if (resolved.IsNothing()) {
+ return Nothing();
+ }
+ return FinalizeResolution(env, resolved.FromJust(), base);
+ } else if (target->IsArray()) {
+ 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);
+ }
+ return Nothing();
+ }
+ for (uint32_t i = 0; i < length; i++) {
+ auto target_item = target_arr->Get(context, i).ToLocalChecked();
+ if (!target_item->IsArray()) {
+ Maybe resolved = ResolveExportsTarget(env, pjson_url,
+ target_item, subpath, pkg_subpath, base, false);
+ if (resolved.IsNothing()) continue;
+ return FinalizeResolution(env, resolved.FromJust(), base);
+ }
+ }
+ if (throw_invalid) {
+ auto invalid = target_arr->Get(context, length - 1).ToLocalChecked();
+ Maybe resolved = ResolveExportsTarget(env, pjson_url, invalid,
+ subpath, pkg_subpath, base, true);
+ CHECK(resolved.IsNothing());
+ }
+ return Nothing();
+ } else if (target->IsObject()) {
+ Local