diff --git a/doc/api/esm.md b/doc/api/esm.md index 992336bd3277e2..be8f867cbea16e 100644 --- a/doc/api/esm.md +++ b/doc/api/esm.md @@ -1207,7 +1207,8 @@ _defaultEnv_ is the conditional environment name priority array, > encoded strings for _"/"_ or _"\\"_, then > 1. Throw an _Invalid Specifier_ error. > 1. Set _selfUrl_ to the result of -> **SELF_REFERENCE_RESOLE**(_packageName_, _packageSubpath_, _parentURL_). +> **SELF_REFERENCE_RESOLE**(_packageName_, _packageSubpath_, _parentURL_, +> **true**). > 1. If _selfUrl_ isn't empty, return _selfUrl_. > 1. If _packageSubpath_ is _undefined_ and _packageName_ is a Node.js builtin > module, then @@ -1230,14 +1231,21 @@ _defaultEnv_ is the conditional environment name priority array, > 1. Return **PACKAGE_EXPORTS_RESOLVE**(_packageURL_, > _packageSubpath_, _pjson.exports_). > 1. Return the URL resolution of _packageSubpath_ in _packageURL_. +> 1. Set _selfUrl_ to the result of +> **SELF_REFERENCE_RESOLE**(_packageName_, _packageSubpath_, _parentURL_, +> **false**). +> 1. If _selfUrl_ isn't empty, return _selfUrl_. > 1. Throw a _Module Not Found_ error. -**SELF_REFERENCE_RESOLVE**(_packageName_, _packageSubpath_, _parentURL_) +**SELF_REFERENCE_RESOLVE**(_packageName_, _packageSubpath_, _parentURL_, + _encapsulated_) > 1. Let _packageURL_ be the result of **READ_PACKAGE_SCOPE**(_parentURL_). > 1. If _packageURL_ is **null**, then -> 1. Return an empty result. +> 1. Return **undefined**. > 1. Let _pjson_ be the result of **READ_PACKAGE_JSON**(_packageURL_). +> 1. If _encapsulated_ is **true** and _pjson_ does not include an +> _"exports"_ property, then return **undefined**. > 1. If _pjson.name_ is equal to _packageName_, then > 1. If _packageSubpath_ is _undefined_, then > 1. Return the result of **PACKAGE_MAIN_RESOLVE**(_packageURL_, _pjson_). @@ -1248,7 +1256,7 @@ _defaultEnv_ is the conditional environment name priority array, > 1. Return **PACKAGE_EXPORTS_RESOLVE**(_packageURL_, _subpath_, > _pjson.exports_). > 1. Return the URL resolution of _subpath_ in _packageURL_. -> 1. Otherwise return an empty result. +> 1. Otherwise, return **undefined**. **PACKAGE_MAIN_RESOLVE**(_packageURL_, _pjson_) diff --git a/doc/api/modules.md b/doc/api/modules.md index 5653fe8f4245bd..3864749e90b92f 100644 --- a/doc/api/modules.md +++ b/doc/api/modules.md @@ -160,9 +160,10 @@ require(X) from module at path Y a. LOAD_AS_FILE(Y + X) b. LOAD_AS_DIRECTORY(Y + X) c. THROW "not found" -4. LOAD_NODE_MODULES(X, dirname(Y)) -5. LOAD_SELF_REFERENCE(X, dirname(Y)) -6. THROW "not found" +4. LOAD_SELF_REFERENCE(X, dirname(Y), true) +5. LOAD_NODE_MODULES(X, dirname(Y)) +6. LOAD_SELF_REFERENCE(X, dirname(Y), false) +7. THROW "not found" LOAD_AS_FILE(X) 1. If X is a file, load X as JavaScript text. STOP @@ -203,11 +204,12 @@ NODE_MODULES_PATHS(START) d. let I = I - 1 5. return DIRS -LOAD_SELF_REFERENCE(X, START) +LOAD_SELF_REFERENCE(X, START, ENCAPSULATED) 1. Find the closest package scope to START. -2. If no scope was found, throw "not found". -3. If the name in `package.json` isn't a prefix of X, throw "not found". -4. Otherwise, resolve the remainder of X relative to this package as if it +2. If no scope was found, return. +3. If ENCAPSULATED is true and the `package.json` has no "exports", return. +4. If the name in `package.json` isn't a prefix of X, throw "not found". +5. Otherwise, resolve the remainder of X relative to this package as if it was loaded via `LOAD_NODE_MODULES` with a name in `package.json`. ``` diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js index 6ddb749ff8d2ce..e2eb83dade0bf9 100644 --- a/lib/internal/modules/cjs/loader.js +++ b/lib/internal/modules/cjs/loader.js @@ -431,13 +431,13 @@ function resolveBasePath(basePath, exts, isMain, trailingSlash, request) { return filename; } -function trySelf(parentPath, isMain, request) { +function trySelf(parentPath, isMain, request, encapsulated) { if (!experimentalSelf) { return false; } const { data: pkg, path: basePath } = readPackageScope(parentPath) || {}; - if (!pkg) return false; + if (!pkg || (encapsulated && 'exports' in pkg === false)) return false; if (typeof pkg.name !== 'string') return false; let expansion; @@ -1003,7 +1003,17 @@ Module._resolveFilename = function(request, parent, isMain, options) { } // Look up the filename first, since that's the cache key. - const filename = Module._findPath(request, paths, isMain); + if (parent && parent.filename) { + const filename = trySelf(parent.filename, isMain, request, true); + if (filename) { + emitExperimentalWarning('Package name self resolution'); + const cacheKey = request + '\x00' + + (paths.length === 1 ? paths[0] : paths.join('\x00')); + Module._pathCache[cacheKey] = filename; + return filename; + } + } + const filename = Module._findPath(request, paths, isMain, false); if (filename) return filename; if (parent && parent.filename) { const filename = trySelf(parent.filename, isMain, request); diff --git a/src/module_wrap.cc b/src/module_wrap.cc index 2ce8fec896d7bc..66a1e33633656d 100644 --- a/src/module_wrap.cc +++ b/src/module_wrap.cc @@ -1152,7 +1152,8 @@ Maybe PackageExportsResolve(Environment* env, Maybe ResolveSelf(Environment* env, const std::string& pkg_name, const std::string& pkg_subpath, - const URL& base) { + const URL& base, + bool encapsulated) { if (!env->options()->experimental_resolve_self) { return Nothing(); } @@ -1172,6 +1173,7 @@ Maybe ResolveSelf(Environment* env, } } if (!found_pjson || pcfg->name != pkg_name) return Nothing(); + if (encapsulated && pcfg->exports.IsEmpty()) return Nothing(); if (!pkg_subpath.length()) { return PackageMainResolve(env, pjson_url, *pcfg, base); } else { @@ -1227,7 +1229,7 @@ Maybe PackageResolve(Environment* env, pkg_subpath = "." + specifier.substr(sep_index); } - Maybe self_url = ResolveSelf(env, pkg_name, pkg_subpath, base); + Maybe self_url = ResolveSelf(env, pkg_name, pkg_subpath, base, true); if (self_url.IsJust()) { ProcessEmitExperimentalWarning(env, "Package name self resolution"); return self_url; @@ -1266,6 +1268,12 @@ Maybe PackageResolve(Environment* env, // Cross-platform root check. } while (pjson_path.length() != last_path.length()); + self_url = ResolveSelf(env, pkg_name, pkg_subpath, base, false); + if (self_url.IsJust()) { + ProcessEmitExperimentalWarning(env, "Package name self resolution"); + return self_url; + } + std::string msg = "Cannot find package '" + pkg_name + "' imported from " + base.ToFilePath(); node::THROW_ERR_MODULE_NOT_FOUND(env, msg.c_str());