diff options
author | Guy Bedford <guybedford@gmail.com> | 2020-08-10 02:54:01 +0300 |
---|---|---|
committer | Guy Bedford <guybedford@gmail.com> | 2020-08-13 21:04:24 +0300 |
commit | f8976a76bbb49d678056d6d2ddd57064cf9e63c2 (patch) | |
tree | 864e198c4ac8ab2cffb21cad70d09b77025abdc0 /lib/internal/modules | |
parent | 56b25e7a6f741c8181c55f3d40d0cab6ffa1eb4d (diff) |
module: share CJS/ESM resolver fns, refactoring
PR-URL: https://github.com/nodejs/node/pull/34744
Reviewed-By: Jan Krems <jan.krems@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Diffstat (limited to 'lib/internal/modules')
-rw-r--r-- | lib/internal/modules/cjs/loader.js | 290 | ||||
-rw-r--r-- | lib/internal/modules/esm/resolve.js | 223 |
2 files changed, 149 insertions, 364 deletions
diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js index 794f84c1285..dcad0bcf632 100644 --- a/lib/internal/modules/cjs/loader.js +++ b/lib/internal/modules/cjs/loader.js @@ -33,13 +33,11 @@ const { Error, JSONParse, Map, - Number, ObjectCreate, ObjectDefineProperty, ObjectFreeze, ObjectGetOwnPropertyDescriptor, ObjectGetPrototypeOf, - ObjectIs, ObjectKeys, ObjectPrototypeHasOwnProperty, ObjectSetPrototypeOf, @@ -49,7 +47,6 @@ const { SafeWeakMap, SafeSet, String, - StringPrototypeIndexOf, StringPrototypeMatch, StringPrototypeSlice, StringPrototypeStartsWith, @@ -94,10 +91,7 @@ let hasLoadedAnyUserCJSModule = false; 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'); @@ -117,7 +111,8 @@ const asyncESM = require('internal/process/esm_loader'); const { kEvaluated } = internalBinding('module_wrap'); const { encodedSepRegEx, - packageInternalResolve + packageExportsResolve, + packageImportsResolve } = require('internal/modules/esm/resolve'); const isWindows = process.platform === 'win32'; @@ -319,18 +314,8 @@ function readPackageScope(checkPath) { return false; } -function readPackageMain(requestPath) { - const pkg = readPackage(requestPath); - return pkg ? pkg.main : undefined; -} - -function readPackageExports(requestPath) { - const pkg = readPackage(requestPath); - return pkg ? pkg.exports : undefined; -} - function tryPackage(requestPath, exts, isMain, originalPath) { - const pkg = readPackageMain(requestPath); + const pkg = readPackage(requestPath)?.main; if (!pkg) { return tryExtensions(path.resolve(requestPath, 'index'), exts, isMain); @@ -435,108 +420,28 @@ function trySelfParentPath(parent) { function trySelf(parentPath, request) { if (!parentPath) return false; - const { data: pkg, path: basePath } = readPackageScope(parentPath) || {}; + const { data: pkg, path: pkgPath } = readPackageScope(parentPath) || {}; if (!pkg || pkg.exports === undefined) return false; if (typeof pkg.name !== 'string') return false; let expansion; if (request === pkg.name) { - expansion = ''; + expansion = '.'; } else if (StringPrototypeStartsWith(request, `${pkg.name}/`)) { - expansion = StringPrototypeSlice(request, pkg.name.length); + expansion = '.' + StringPrototypeSlice(request, pkg.name.length); } else { return false; } - const fromExports = applyExports(basePath, expansion); - if (fromExports) { - return tryFile(fromExports, false); - } - assert(fromExports !== false); -} - -function isConditionalDotExportSugar(exports, basePath) { - if (typeof exports === 'string') - return true; - if (ArrayIsArray(exports)) - return true; - if (typeof exports !== 'object') - return false; - let isConditional = false; - let firstCheck = true; - for (const key of ObjectKeys(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 mappingKey = `.${expansion}`; - - let pkgExports = readPackageExports(basePath); - if (pkgExports === undefined || pkgExports === null) - return false; - - if (isConditionalDotExportSugar(pkgExports, basePath)) - pkgExports = { '.': pkgExports }; - - if (typeof pkgExports === 'object') { - if (ObjectPrototypeHasOwnProperty(pkgExports, mappingKey)) { - const mapping = pkgExports[mappingKey]; - const resolved = resolveExportsTarget( - pathToFileURL(basePath + '/'), mapping, '', mappingKey); - if (resolved === null || resolved === undefined) - throw new ERR_PACKAGE_PATH_NOT_EXPORTED( - basePath, mappingKey); - return resolved; - } - - let dirMatch = ''; - for (const candidateKey of ObjectKeys(pkgExports)) { - if (candidateKey[candidateKey.length - 1] !== '/') continue; - if (candidateKey.length > dirMatch.length && - StringPrototypeStartsWith(mappingKey, candidateKey)) { - dirMatch = candidateKey; - } - } - - if (dirMatch !== '') { - const mapping = pkgExports[dirMatch]; - const subpath = StringPrototypeSlice(mappingKey, dirMatch.length); - const resolved = resolveExportsTarget(pathToFileURL(basePath + '/'), - mapping, subpath, mappingKey); - if (resolved === null || resolved === undefined) - throw new ERR_PACKAGE_PATH_NOT_EXPORTED( - basePath, mappingKey + subpath); - // Extension searching for folder exports only - const rc = stat(resolved); - if (rc === 0) return resolved; - if (!(RegExpPrototypeTest(trailingSlashRegex, resolved))) { - const exts = ObjectKeys(Module._extensions); - const filename = tryExtensions(resolved, exts, false); - if (filename) return filename; - } - if (rc === 1) { - const exts = ObjectKeys(Module._extensions); - const filename = tryPackage(resolved, exts, false, - basePath + expansion); - if (filename) return filename; - } - // Undefined means not found - return; - } + try { + return finalizeEsmResolution(packageExportsResolve( + pathToFileURL(pkgPath + '/package.json'), expansion, pkg, + pathToFileURL(parentPath), cjsConditions), request, parentPath, pkgPath); + } catch (e) { + if (e.code === 'ERR_MODULE_NOT_FOUND') + throw createEsmNotFoundErr(request, pkgPath + '/package.json'); + throw e; } - - throw new ERR_PACKAGE_PATH_NOT_EXPORTED(basePath, mappingKey); } // This only applies to requests of a specific form: @@ -547,107 +452,21 @@ function resolveExports(nmPath, request) { // The implementation's behavior is meant to mirror resolution in ESM. const [, name, expansion = ''] = StringPrototypeMatch(request, EXPORTS_PATTERN) || []; - if (!name) { - return false; - } - - const basePath = path.resolve(nmPath, name); - const fromExports = applyExports(basePath, expansion); - if (fromExports) { - return tryFile(fromExports, false); - } - return fromExports; -} - -function isArrayIndex(p) { - assert(typeof p === 'string'); - const n = Number(p); - if (String(n) !== p) - return false; - if (ObjectIs(n, +0)) - return true; - if (!Number.isInteger(n)) - return false; - return n >= 0 && n < (2 ** 32) - 1; -} - -function resolveExportsTarget(baseUrl, target, subpath, mappingKey) { - if (typeof target === 'string') { - 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) - resolvedTarget = undefined; - } - if (subpath.length > 0 && target[target.length - 1] !== '/') - resolvedTarget = undefined; - if (resolvedTarget === undefined) - throw new ERR_INVALID_PACKAGE_TARGET(baseUrl.pathname, mappingKey, - target); - const resolved = new URL(subpath, resolvedTarget); - const resolvedPath = resolved.pathname; - if (StringPrototypeStartsWith(resolvedPath, resolvedTargetPath) && - StringPrototypeIndexOf(resolvedPath, '/node_modules/', - pkgPathPath.length - 1) === -1) { - if (StringPrototypeMatch(resolvedPath, encodedSepRegEx)) - throw new ERR_INVALID_MODULE_SPECIFIER( - resolvedPath, 'must not include encoded "/" or "\\" characters', - fileURLToPath(baseUrl)); - return fileURLToPath(resolved); - } - const reason = 'request is not a valid subpath for the "exports" ' + - `resolution of ${baseUrl.pathname}package.json`; - throw new ERR_INVALID_MODULE_SPECIFIER(mappingKey + subpath, reason); - } else if (ArrayIsArray(target)) { - if (target.length === 0) - return null; - let lastException; - for (const targetValue of target) { - let resolved; - try { - resolved = resolveExportsTarget(baseUrl, targetValue, subpath, - mappingKey); - } catch (e) { - lastException = e; - if (e.code !== 'ERR_INVALID_PACKAGE_TARGET') - throw e; - } - if (resolved === undefined) - continue; - if (resolved === null) { - lastException = null; - continue; - } - return resolved; - } - // Throw last fallback error - if (lastException === undefined || lastException === null) - return lastException; - throw lastException; - } else if (typeof target === 'object' && target !== null) { - const keys = ObjectKeys(target); - if (keys.some(isArrayIndex)) { - throw new ERR_INVALID_PACKAGE_CONFIG(baseUrl, '"exports" cannot ' + - 'contain numeric property keys.'); - } - for (const p of keys) { - if (cjsConditions.has(p) || p === 'default') { - const resolved = resolveExportsTarget(baseUrl, target[p], subpath, - mappingKey); - if (resolved === undefined) - continue; - return resolved; - } + if (!name) + return; + const pkgPath = path.resolve(nmPath, name); + const pkg = readPackage(pkgPath); + if (pkg && pkg.exports !== null && pkg.exports !== undefined) { + try { + return finalizeEsmResolution(packageExportsResolve( + pathToFileURL(pkgPath + '/package.json'), '.' + expansion, pkg, null, + cjsConditions), request, null, pkgPath); + } catch (e) { + if (e.code === 'ERR_MODULE_NOT_FOUND') + throw createEsmNotFoundErr(request, pkgPath + '/package.json'); + throw e; } - return undefined; - } else if (target === null) { - return null; } - throw new ERR_INVALID_PACKAGE_TARGET(baseUrl.pathname, mappingKey, target); } const trailingSlashRegex = /(?:^|\/)\.?\.$/; @@ -680,12 +499,8 @@ Module._findPath = function(request, paths, isMain) { if (!absoluteRequest) { const exportsResolved = resolveExports(curPath, request); - // Undefined means not found, false means no exports - if (exportsResolved === undefined) - break; - if (exportsResolved) { + if (exportsResolved) return exportsResolved; - } } const basePath = path.resolve(curPath, request); @@ -1050,19 +865,14 @@ Module._resolveFilename = function(request, parent, isMain, options) { if (pkg.data && pkg.data.imports !== null && pkg.data.imports !== undefined) { try { - const resolved = packageInternalResolve( - request, pathToFileURL(parent.filename), cjsConditions); - return fileURLToPath(resolved); - } catch (err) { - if (err.code === 'ERR_MODULE_NOT_FOUND') { - // eslint-disable-next-line no-restricted-syntax - const err = new Error(`Cannot find module '${request}'`); - err.code = 'MODULE_NOT_FOUND'; - err.path = path.resolve(pkg.path, 'package.json'); - // TODO(BridgeAR): Add the requireStack as well. - throw err; - } - throw err; + return finalizeEsmResolution( + packageImportsResolve(request, pathToFileURL(parent.filename), + cjsConditions), request, parent.filename, + pkg.path); + } catch (e) { + if (e.code === 'ERR_MODULE_NOT_FOUND') + throw createEsmNotFoundErr(request); + throw e; } } } @@ -1098,6 +908,34 @@ Module._resolveFilename = function(request, parent, isMain, options) { throw err; }; +function finalizeEsmResolution(match, request, parentPath, pkgPath) { + const { resolved, exact } = match; + if (StringPrototypeMatch(resolved, encodedSepRegEx)) + throw new ERR_INVALID_MODULE_SPECIFIER( + resolved, 'must not include encoded "/" or "\\" characters', parentPath); + const filename = fileURLToPath(resolved); + let actual = tryFile(filename); + if (!exact && !actual) { + const exts = ObjectKeys(Module._extensions); + actual = tryExtensions(filename, exts, false) || + tryPackage(filename, exts, false, request); + } + if (actual) + return actual; + const err = createEsmNotFoundErr(filename, + path.resolve(pkgPath, 'package.json')); + throw err; +} + +function createEsmNotFoundErr(request, path) { + // eslint-disable-next-line no-restricted-syntax + const err = new Error(`Cannot find module '${request}'`); + err.code = 'MODULE_NOT_FOUND'; + if (path) + err.path = path; + // TODO(BridgeAR): Add the requireStack as well. + return err; +} // Given a file name, pass it to the proper extension handler. Module.prototype.load = function(filename) { diff --git a/lib/internal/modules/esm/resolve.js b/lib/internal/modules/esm/resolve.js index 7cf35529481..f6879465451 100644 --- a/lib/internal/modules/esm/resolve.js +++ b/lib/internal/modules/esm/resolve.js @@ -15,7 +15,6 @@ const { SafeSet, String, StringPrototypeEndsWith, - StringPrototypeIncludes, StringPrototypeIndexOf, StringPrototypeReplace, StringPrototypeSlice, @@ -78,14 +77,6 @@ function tryStatSync(path) { } } -/** - * - * '/foo/package.json' -> '/foo' - */ -function removePackageJsonFromPath(path) { - return StringPrototypeSlice(path, 0, path.length - 13); -} - function getPackageConfig(path) { const existing = packageJSONCache.get(path); if (existing !== undefined) { @@ -110,8 +101,7 @@ function getPackageConfig(path) { try { packageJSON = JSONParse(source); } catch (error) { - const errorPath = removePackageJsonFromPath(path); - throw new ERR_INVALID_PACKAGE_CONFIG(errorPath, error.message, true); + throw new ERR_INVALID_PACKAGE_CONFIG(path, null, error.message); } let { imports, main, name, type } = packageJSON; @@ -177,7 +167,7 @@ function fileExists(url) { return tryStatSync(fileURLToPath(url)).isFile(); } -function legacyMainResolve(packageJSONUrl, packageConfig) { +function legacyMainResolve(packageJSONUrl, packageConfig, base) { let guess; if (packageConfig.main !== undefined) { // Note: fs check redundances will be handled by Descriptor cache here. @@ -222,7 +212,8 @@ function legacyMainResolve(packageJSONUrl, packageConfig) { return guess; } // Not found. - return undefined; + throw new ERR_MODULE_NOT_FOUND( + fileURLToPath(new URL('.', packageJSONUrl)), fileURLToPath(base)); } function resolveExtensionsWithTryExactName(search) { @@ -246,35 +237,34 @@ function resolveIndex(search) { const encodedSepRegEx = /%2F|%2C/i; function finalizeResolution(resolved, base) { + if (RegExpPrototypeTest(encodedSepRegEx, resolved.pathname)) + throw new ERR_INVALID_MODULE_SPECIFIER( + resolved.pathname, 'must not include encoded "/" or "\\" characters', + fileURLToPath(base)); + + const path = fileURLToPath(resolved); if (getOptionValue('--experimental-specifier-resolution') === 'node') { let file = resolveExtensionsWithTryExactName(resolved); if (file !== undefined) return file; - if (!StringPrototypeEndsWith(resolved.pathname, '/')) { - file = resolveIndex(new URL(`${resolved.pathname}/`, base)); + if (!StringPrototypeEndsWith(path, '/')) { + file = resolveIndex(new URL(`${resolved}/`)); + if (file !== undefined) return file; } else { - file = resolveIndex(resolved); + return resolveIndex(resolved) || resolved; } - if (file !== undefined) return file; throw new ERR_MODULE_NOT_FOUND( resolved.pathname, fileURLToPath(base), 'module'); } - if (RegExpPrototypeTest(encodedSepRegEx, resolved.pathname)) - throw new ERR_INVALID_MODULE_SPECIFIER( - resolved.pathname, 'must not include encoded "/" or "\\" characters', - fileURLToPath(base)); - - const path = fileURLToPath(resolved); - const stats = tryStatSync(path); - + const stats = tryStatSync(StringPrototypeEndsWith(path, '/') ? + StringPrototypeSlice(path, -1) : path); if (stats.isDirectory()) { - const err = new ERR_UNSUPPORTED_DIR_IMPORT( - path || resolved.pathname, fileURLToPath(base)); + const err = new ERR_UNSUPPORTED_DIR_IMPORT(path, fileURLToPath(base)); err.url = String(resolved); throw err; } else if (!stats.isFile()) { throw new ERR_MODULE_NOT_FOUND( - path || resolved.pathname, fileURLToPath(base), 'module'); + path || resolved.pathname, base && fileURLToPath(base), 'module'); } return resolved; @@ -288,14 +278,15 @@ function throwImportNotDefined(specifier, packageJSONUrl, base) { function throwExportsNotFound(subpath, packageJSONUrl, base) { throw new ERR_PACKAGE_PATH_NOT_EXPORTED( - fileURLToPath(new URL('.', packageJSONUrl)), subpath, fileURLToPath(base)); + fileURLToPath(new URL('.', packageJSONUrl)), subpath, + base && fileURLToPath(base)); } function throwInvalidSubpath(subpath, packageJSONUrl, internal, base) { const reason = `request is not a valid subpath for the "${internal ? - 'imports' : 'exports'}" resolution of ${fileURLToPath(packageJSONUrl)}${ - base ? ` imported from ${base}` : ''}`; - throw new ERR_INVALID_MODULE_SPECIFIER(subpath, reason, fileURLToPath(base)); + 'imports' : 'exports'}" resolution of ${fileURLToPath(packageJSONUrl)}`; + throw new ERR_INVALID_MODULE_SPECIFIER(subpath, reason, + base && fileURLToPath(base)); } function throwInvalidPackageTarget( @@ -307,44 +298,46 @@ function throwInvalidPackageTarget( } throw new ERR_INVALID_PACKAGE_TARGET( fileURLToPath(new URL('.', packageJSONUrl)), subpath, target, - internal, fileURLToPath(base)); + internal, base && fileURLToPath(base)); } +const invalidSegmentRegEx = /(^|\\|\/)(\.\.?|node_modules)(\\|\/|$)/; + function resolvePackageTargetString( target, subpath, match, packageJSONUrl, base, internal, conditions) { if (subpath !== '' && target[target.length - 1] !== '/') throwInvalidPackageTarget(match, target, packageJSONUrl, internal, base); - if (!target.startsWith('./')) { - if (internal && !target.startsWith('../') && !target.startsWith('/')) { + if (!StringPrototypeStartsWith(target, './')) { + if (internal && !StringPrototypeStartsWith(target, '../') && + !StringPrototypeStartsWith(target, '/')) { let isURL = false; try { new URL(target); isURL = true; } catch {} if (!isURL) - return packageResolve(target + subpath, base, conditions); + return packageResolve(target + subpath, packageJSONUrl, conditions); } throwInvalidPackageTarget(match, target, packageJSONUrl, internal, base); } + if (RegExpPrototypeTest(invalidSegmentRegEx, StringPrototypeSlice(target, 2))) + throwInvalidPackageTarget(match, target, packageJSONUrl, internal, base); + const resolved = new URL(target, packageJSONUrl); const resolvedPath = resolved.pathname; const packagePath = new URL('.', packageJSONUrl).pathname; - if (!StringPrototypeStartsWith(resolvedPath, packagePath) || - StringPrototypeIncludes( - resolvedPath, '/node_modules/', packagePath.length - 1)) + if (!StringPrototypeStartsWith(resolvedPath, packagePath)) throwInvalidPackageTarget(match, target, packageJSONUrl, internal, base); if (subpath === '') return resolved; - const subpathResolved = new URL(subpath, resolved); - const subpathResolvedPath = subpathResolved.pathname; - if (!StringPrototypeStartsWith(subpathResolvedPath, resolvedPath) || - StringPrototypeIncludes(subpathResolvedPath, - '/node_modules/', packagePath.length - 1)) + + if (RegExpPrototypeTest(invalidSegmentRegEx, subpath)) throwInvalidSubpath(match + subpath, packageJSONUrl, internal, base); - return subpathResolved; + + return new URL(subpath, resolved); } /** @@ -357,12 +350,12 @@ function isArrayIndex(key) { return keyNum >= 0 && keyNum < 0xFFFF_FFFF; } -function resolvePackageTarget( - packageJSONUrl, target, subpath, packageSubpath, base, internal, conditions) { +function resolvePackageTarget(packageJSONUrl, target, subpath, packageSubpath, + base, internal, conditions) { if (typeof target === 'string') { - return finalizeResolution(resolvePackageTargetString( + return resolvePackageTargetString( target, subpath, packageSubpath, packageJSONUrl, base, internal, - conditions), base); + conditions); } else if (ArrayIsArray(target)) { if (target.length === 0) return null; @@ -387,7 +380,7 @@ function resolvePackageTarget( lastException = null; continue; } - return finalizeResolution(resolved, base); + return resolved; } if (lastException === undefined || lastException === null) return lastException; @@ -398,8 +391,8 @@ function resolvePackageTarget( const key = keys[i]; if (isArrayIndex(key)) { throw new ERR_INVALID_PACKAGE_CONFIG( - fileURLToPath(packageJSONUrl), - '"exports" cannot contain numeric property keys'); + fileURLToPath(packageJSONUrl), base, + '"exports" cannot contain numeric property keys.'); } } for (let i = 0; i < keys.length; i++) { @@ -436,7 +429,7 @@ function isConditionalExportsMainSugar(exports, packageJSONUrl, base) { isConditionalSugar = curIsConditionalSugar; } else if (isConditionalSugar !== curIsConditionalSugar) { throw new ERR_INVALID_PACKAGE_CONFIG( - fileURLToPath(packageJSONUrl), + fileURLToPath(packageJSONUrl), base, '"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.'); @@ -445,44 +438,6 @@ function isConditionalExportsMainSugar(exports, packageJSONUrl, base) { return isConditionalSugar; } -function packageMainResolve(packageJSONUrl, packageConfig, base, conditions) { - if (packageConfig.exists) { - const exports = packageConfig.exports; - if (exports !== undefined) { - if (isConditionalExportsMainSugar(exports, packageJSONUrl, base)) { - const resolved = resolvePackageTarget(packageJSONUrl, exports, '', '', - base, false, conditions); - if (resolved === null || resolved === undefined) - throwExportsNotFound('.', packageJSONUrl, base); - return resolved; - } else if (typeof exports === 'object' && exports !== null) { - const target = exports['.']; - if (target !== undefined) { - const resolved = resolvePackageTarget(packageJSONUrl, target, '', '', - base, false, conditions); - if (resolved === null || resolved === undefined) - throwExportsNotFound('.', packageJSONUrl, base); - return resolved; - } - } - - throw new ERR_PACKAGE_PATH_NOT_EXPORTED(packageJSONUrl, '.'); - } - if (getOptionValue('--experimental-specifier-resolution') === 'node') { - if (packageConfig.main !== undefined) { - return finalizeResolution( - new URL(packageConfig.main, packageJSONUrl), base); - } - return finalizeResolution( - new URL('index', packageJSONUrl), base); - } - return legacyMainResolve(packageJSONUrl, packageConfig); - } - - throw new ERR_MODULE_NOT_FOUND( - fileURLToPath(new URL('.', packageJSONUrl)), fileURLToPath(base)); -} - /** * @param {URL} packageJSONUrl * @param {string} packageSubpath @@ -493,28 +448,27 @@ function packageMainResolve(packageJSONUrl, packageConfig, base, conditions) { */ function packageExportsResolve( packageJSONUrl, packageSubpath, packageConfig, base, conditions) { - const exports = packageConfig.exports; - if (exports === undefined || - isConditionalExportsMainSugar(exports, packageJSONUrl, base)) { - throwExportsNotFound(packageSubpath, packageJSONUrl, base); - } + let exports = packageConfig.exports; + if (isConditionalExportsMainSugar(exports, packageJSONUrl, base)) + exports = { '.': exports }; if (ObjectPrototypeHasOwnProperty(exports, packageSubpath)) { const target = exports[packageSubpath]; const resolved = resolvePackageTarget( - packageJSONUrl, target, '', packageSubpath, base, false, conditions); + packageJSONUrl, target, '', packageSubpath, base, false, conditions + ); if (resolved === null || resolved === undefined) throwExportsNotFound(packageSubpath, packageJSONUrl, base); - return finalizeResolution(resolved, base); + return { resolved, exact: true }; } let bestMatch = ''; const keys = ObjectGetOwnPropertyNames(exports); for (let i = 0; i < keys.length; i++) { const key = keys[i]; - if (key[key.length - 1] !== '/') continue; - if (StringPrototypeStartsWith(packageSubpath, key) && - key.length > bestMatch.length) { + if (key[key.length - 1] === '/' && + StringPrototypeStartsWith(packageSubpath, key) && + key.length > bestMatch.length) { bestMatch = key; } } @@ -522,18 +476,18 @@ function packageExportsResolve( if (bestMatch) { const target = exports[bestMatch]; const subpath = StringPrototypeSubstr(packageSubpath, bestMatch.length); - const resolved = resolvePackageTarget( - packageJSONUrl, target, subpath, bestMatch, base, false, conditions); + const resolved = resolvePackageTarget(packageJSONUrl, target, subpath, + bestMatch, base, false, conditions); if (resolved === null || resolved === undefined) throwExportsNotFound(packageSubpath, packageJSONUrl, base); - return finalizeResolution(resolved, base); + return { resolved, exact: false }; } throwExportsNotFound(packageSubpath, packageJSONUrl, base); } -function packageInternalResolve(name, base, conditions) { - if (name === '#' || name.startsWith('#/')) { +function packageImportsResolve(name, base, conditions) { + if (name === '#' || StringPrototypeStartsWith(name, '#/')) { const reason = 'is not a valid internal imports specifier name'; throw new ERR_INVALID_MODULE_SPECIFIER(name, reason, fileURLToPath(base)); } @@ -545,17 +499,18 @@ function packageInternalResolve(name, base, conditions) { if (imports) { if (ObjectPrototypeHasOwnProperty(imports, name)) { const resolved = resolvePackageTarget( - packageJSONUrl, imports[name], '', name, base, true, conditions); + packageJSONUrl, imports[name], '', name, base, true, conditions + ); if (resolved !== null) - return finalizeResolution(resolved, base); + return { resolved, exact: true }; } else { let bestMatch = ''; const keys = ObjectGetOwnPropertyNames(imports); for (let i = 0; i < keys.length; i++) { const key = keys[i]; - if (key[key.length - 1] !== '/') continue; - if (StringPrototypeStartsWith(name, key) && - key.length > bestMatch.length) { + if (key[key.length - 1] === '/' && + StringPrototypeStartsWith(name, key) && + key.length > bestMatch.length) { bestMatch = key; } } @@ -564,10 +519,9 @@ function packageInternalResolve(name, base, conditions) { const target = imports[bestMatch]; const subpath = StringPrototypeSubstr(name, bestMatch.length); const resolved = resolvePackageTarget( - packageJSONUrl, target, subpath, bestMatch, base, true, - conditions); + packageJSONUrl, target, subpath, bestMatch, base, true, conditions); if (resolved !== null) - return finalizeResolution(resolved, base); + return { resolved, exact: false }; } } } @@ -617,23 +571,18 @@ function packageResolve(specifier, base, conditions) { specifier, 'is not a valid package name', fileURLToPath(base)); } - const packageSubpath = separatorIndex === -1 ? - '' : '.' + StringPrototypeSlice(specifier, separatorIndex); + const packageSubpath = '.' + (separatorIndex === -1 ? '' : + StringPrototypeSlice(specifier, separatorIndex)); // ResolveSelf const packageConfig = getPackageScopeConfig(base, base); if (packageConfig.exists) { const packageJSONUrl = pathToFileURL(packageConfig.pjsonPath); if (packageConfig.name === packageName && - packageConfig.exports !== undefined) { - if (packageSubpath === './') { - return new URL('./', packageJSONUrl); - } else if (packageSubpath === '') { - return packageMainResolve(packageJSONUrl, packageConfig, base, - conditions); - } + packageConfig.exports !== undefined && packageConfig.exports !== null) { return packageExportsResolve( - packageJSONUrl, packageSubpath, packageConfig, base, conditions); + packageJSONUrl, packageSubpath, packageConfig, base, conditions + ).resolved; } } @@ -642,7 +591,8 @@ function packageResolve(specifier, base, conditions) { let packageJSONPath = fileURLToPath(packageJSONUrl); let lastPath; do { - const stat = tryStatSync(removePackageJsonFromPath(packageJSONPath)); + const stat = tryStatSync(StringPrototypeSlice(packageJSONPath, 0, + packageJSONPath.length - 13)); if (!stat.isDirectory()) { lastPath = packageJSONPath; packageJSONUrl = new URL((isScoped ? @@ -654,17 +604,13 @@ function packageResolve(specifier, base, conditions) { // Package match. const packageConfig = getPackageConfig(packageJSONPath, base); - if (packageSubpath === './') { - return new URL('./', packageJSONUrl); - } else if (packageSubpath === '') { - return packageMainResolve(packageJSONUrl, packageConfig, base, - conditions); - } else if (packageConfig.exports !== undefined) { + if (packageConfig.exports !== undefined && packageConfig.exports !== null) return packageExportsResolve( - packageJSONUrl, packageSubpath, packageConfig, base, conditions); - } - return finalizeResolution( - new URL(packageSubpath, packageJSONUrl), base); + packageJSONUrl, packageSubpath, packageConfig, base, conditions + ).resolved; + if (packageSubpath === '.') + return legacyMainResolve(packageJSONUrl, packageConfig, base); + return new URL(packageSubpath, packageJSONUrl); // Cross-platform root check. } while (packageJSONPath.length !== lastPath.length); @@ -706,12 +652,12 @@ function moduleResolve(specifier, base, conditions) { if (shouldBeTreatedAsRelativeOrAbsolutePath(specifier)) { resolved = new URL(specifier, base); } else if (specifier[0] === '#') { - resolved = packageInternalResolve(specifier, base, conditions); + ({ resolved } = packageImportsResolve(specifier, base, conditions)); } else { try { resolved = new URL(specifier); } catch { - return packageResolve(specifier, base, conditions); + resolved = packageResolve(specifier, base, conditions); } } return finalizeResolution(resolved, base); @@ -847,5 +793,6 @@ module.exports = { defaultResolve, encodedSepRegEx, getPackageType, - packageInternalResolve + packageExportsResolve, + packageImportsResolve }; |