summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorGuy Bedford <guybedford@gmail.com>2019-08-02 01:30:32 -0400
committerGuy Bedford <guybedford@gmail.com>2019-08-12 06:24:28 -0400
commit2103ae483547831281e1e3882029e37d445689a7 (patch)
tree78199f21a7898ea63ef1e64b85c8f74c6b669171 /lib
parent15b2d1331082c66adf608bb4de9987aa47a25358 (diff)
downloadnode-new-2103ae483547831281e1e3882029e37d445689a7.tar.gz
module: pkg exports validations and fallbacks
PR-URL: https://github.com/nodejs/node/pull/28949 Reviewed-By: Bradley Farias <bradley.meck@gmail.com> Reviewed-By: Jan Krems <jan.krems@gmail.com>
Diffstat (limited to 'lib')
-rw-r--r--lib/internal/modules/cjs/loader.js66
1 files changed, 51 insertions, 15 deletions
diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js
index 9332c6c277..5d5767e0cf 100644
--- a/lib/internal/modules/cjs/loader.js
+++ b/lib/internal/modules/cjs/loader.js
@@ -24,6 +24,7 @@
const {
JSON,
Object,
+ ObjectPrototype,
Reflect,
SafeMap,
StringPrototype,
@@ -348,18 +349,18 @@ function resolveExports(nmPath, request, absoluteRequest) {
const basePath = path.resolve(nmPath, name);
const pkgExports = readExports(basePath);
+ const mappingKey = `.${expansion}`;
- if (pkgExports != null) {
- const mappingKey = `.${expansion}`;
- const mapping = pkgExports[mappingKey];
- if (typeof mapping === 'string') {
- return fileURLToPath(new URL(mapping, `${pathToFileURL(basePath)}/`));
+ if (typeof pkgExports === 'object' && pkgExports !== null) {
+ if (ObjectPrototype.hasOwnProperty(pkgExports, mappingKey)) {
+ const mapping = pkgExports[mappingKey];
+ return resolveExportsTarget(pathToFileURL(basePath + '/'), mapping, '',
+ basePath, mappingKey);
}
let dirMatch = '';
- for (const [candidateKey, candidateValue] of Object.entries(pkgExports)) {
+ for (const candidateKey of Object.keys(pkgExports)) {
if (candidateKey[candidateKey.length - 1] !== '/') continue;
- if (candidateValue[candidateValue.length - 1] !== '/') continue;
if (candidateKey.length > dirMatch.length &&
StringPrototype.startsWith(mappingKey, candidateKey)) {
dirMatch = candidateKey;
@@ -367,15 +368,13 @@ function resolveExports(nmPath, request, absoluteRequest) {
}
if (dirMatch !== '') {
- const dirMapping = pkgExports[dirMatch];
- const remainder = StringPrototype.slice(mappingKey, dirMatch.length);
- const expectedPrefix =
- new URL(dirMapping, `${pathToFileURL(basePath)}/`);
- const resolved = new URL(remainder, expectedPrefix).href;
- if (StringPrototype.startsWith(resolved, expectedPrefix.href)) {
- return fileURLToPath(resolved);
- }
+ const mapping = pkgExports[dirMatch];
+ const subpath = StringPrototype.slice(mappingKey, dirMatch.length);
+ return resolveExportsTarget(pathToFileURL(basePath + '/'), mapping,
+ subpath, 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`);
@@ -387,6 +386,43 @@ function resolveExports(nmPath, request, absoluteRequest) {
return path.resolve(nmPath, request);
}
+function resolveExportsTarget(pkgPath, target, subpath, basePath, 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 (StringPrototype.startsWith(resolvedTargetPath, pkgPathPath) &&
+ StringPrototype.indexOf(resolvedTargetPath, '/node_modules/',
+ pkgPathPath.length - 1) === -1) {
+ const resolved = new URL(subpath, resolvedTarget);
+ const resolvedPath = resolved.pathname;
+ if (StringPrototype.startsWith(resolvedPath, resolvedTargetPath) &&
+ StringPrototype.indexOf(resolvedPath, '/node_modules/',
+ pkgPathPath.length - 1) === -1) {
+ return fileURLToPath(resolved);
+ }
+ }
+ }
+ } else if (Array.isArray(target)) {
+ for (const targetValue of target) {
+ if (typeof targetValue !== 'string') continue;
+ try {
+ return resolveExportsTarget(pkgPath, targetValue, subpath, basePath,
+ mappingKey);
+ } catch (e) {
+ if (e.code !== 'MODULE_NOT_FOUND') throw e;
+ }
+ }
+ }
+ // 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;
+}
+
Module._findPath = function(request, paths, isMain) {
const absoluteRequest = path.isAbsolute(request);
if (absoluteRequest) {