diff options
author | Guy Bedford <guybedford@gmail.com> | 2019-08-02 01:30:32 -0400 |
---|---|---|
committer | Guy Bedford <guybedford@gmail.com> | 2019-08-12 06:24:28 -0400 |
commit | 2103ae483547831281e1e3882029e37d445689a7 (patch) | |
tree | 78199f21a7898ea63ef1e64b85c8f74c6b669171 /lib | |
parent | 15b2d1331082c66adf608bb4de9987aa47a25358 (diff) | |
download | node-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.js | 66 |
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) { |