-
-
Notifications
You must be signed in to change notification settings - Fork 31.9k
module: fix extension searching for exports #32351
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -37,6 +37,7 @@ const { | |
ObjectPrototypeHasOwnProperty, | ||
ObjectSetPrototypeOf, | ||
ReflectSet, | ||
RegExpPrototypeTest, | ||
SafeMap, | ||
String, | ||
StringPrototypeIndexOf, | ||
|
@@ -125,7 +126,7 @@ function enrichCJSError(err) { | |
after a comment block and/or after a variable definition. | ||
*/ | ||
if (err.message.startsWith('Unexpected token \'export\'') || | ||
(/^\s*import(?=[ {'"*])\s*(?![ (])/).test(lineWithErr)) { | ||
(RegExpPrototypeTest(/^\s*import(?=[ {'"*])\s*(?![ (])/, lineWithErr))) { | ||
// Emit the warning synchronously because we are in the middle of handling | ||
// a SyntaxError that will throw and likely terminate the process before an | ||
// asynchronous warning would be emitted. | ||
|
@@ -352,10 +353,11 @@ const realpathCache = new Map(); | |
// absolute realpath. | ||
function tryFile(requestPath, isMain) { | ||
const rc = stat(requestPath); | ||
if (rc !== 0) return; | ||
if (preserveSymlinks && !isMain) { | ||
return rc === 0 && path.resolve(requestPath); | ||
return path.resolve(requestPath); | ||
} | ||
return rc === 0 && toRealPath(requestPath); | ||
return toRealPath(requestPath); | ||
} | ||
|
||
function toRealPath(requestPath) { | ||
|
@@ -392,52 +394,7 @@ function findLongestRegisteredExtension(filename) { | |
return '.js'; | ||
} | ||
|
||
function resolveBasePath(basePath, exts, isMain, trailingSlash, request) { | ||
let filename; | ||
|
||
const rc = stat(basePath); | ||
if (!trailingSlash) { | ||
if (rc === 0) { // File. | ||
if (!isMain) { | ||
if (preserveSymlinks) { | ||
filename = path.resolve(basePath); | ||
} else { | ||
filename = toRealPath(basePath); | ||
} | ||
} else if (preserveSymlinksMain) { | ||
// For the main module, we use the preserveSymlinksMain flag instead | ||
// mainly for backward compatibility, as the preserveSymlinks flag | ||
// historically has not applied to the main module. Most likely this | ||
// was intended to keep .bin/ binaries working, as following those | ||
// symlinks is usually required for the imports in the corresponding | ||
// files to resolve; that said, in some use cases following symlinks | ||
// causes bigger problems which is why the preserveSymlinksMain option | ||
// is needed. | ||
filename = path.resolve(basePath); | ||
} else { | ||
filename = toRealPath(basePath); | ||
} | ||
} | ||
|
||
if (!filename) { | ||
// Try it with each of the extensions | ||
if (exts === undefined) | ||
exts = ObjectKeys(Module._extensions); | ||
filename = tryExtensions(basePath, exts, isMain); | ||
} | ||
} | ||
|
||
if (!filename && rc === 1) { // Directory. | ||
// try it with each of the extensions at "index" | ||
if (exts === undefined) | ||
exts = ObjectKeys(Module._extensions); | ||
filename = tryPackage(basePath, exts, isMain, request); | ||
} | ||
|
||
return filename; | ||
} | ||
|
||
function trySelf(parentPath, isMain, request) { | ||
function trySelf(parentPath, request) { | ||
const { data: pkg, path: basePath } = readPackageScope(parentPath) || {}; | ||
if (!pkg || pkg.exports === undefined) return false; | ||
if (typeof pkg.name !== 'string') return false; | ||
|
@@ -451,20 +408,11 @@ function trySelf(parentPath, isMain, request) { | |
return false; | ||
} | ||
|
||
const exts = ObjectKeys(Module._extensions); | ||
const fromExports = applyExports(basePath, expansion); | ||
// Use exports | ||
if (fromExports) { | ||
let trailingSlash = request.length > 0 && | ||
request.charCodeAt(request.length - 1) === CHAR_FORWARD_SLASH; | ||
if (!trailingSlash) { | ||
trailingSlash = /(?:^|\/)\.?\.$/.test(request); | ||
} | ||
return resolveBasePath(fromExports, exts, isMain, trailingSlash, request); | ||
} else { | ||
// Use main field | ||
return tryPackage(basePath, exts, isMain, request); | ||
return tryFile(fromExports, false); | ||
} | ||
assert(fromExports !== false); | ||
} | ||
|
||
function isConditionalDotExportSugar(exports, basePath) { | ||
|
@@ -496,7 +444,7 @@ function applyExports(basePath, expansion) { | |
|
||
let pkgExports = readPackageExports(basePath); | ||
if (pkgExports === undefined || pkgExports === null) | ||
return path.resolve(basePath, mappingKey); | ||
return false; | ||
|
||
if (isConditionalDotExportSugar(pkgExports, basePath)) | ||
pkgExports = { '.': pkgExports }; | ||
|
@@ -520,8 +468,24 @@ function applyExports(basePath, expansion) { | |
if (dirMatch !== '') { | ||
const mapping = pkgExports[dirMatch]; | ||
const subpath = StringPrototypeSlice(mappingKey, dirMatch.length); | ||
return resolveExportsTarget(pathToFileURL(basePath + '/'), mapping, | ||
subpath, mappingKey); | ||
const resolved = resolveExportsTarget(pathToFileURL(basePath + '/'), | ||
mapping, subpath, mappingKey); | ||
// 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; | ||
} | ||
} | ||
|
||
|
@@ -532,20 +496,20 @@ function applyExports(basePath, expansion) { | |
// 1. name/.* | ||
// 2. @scope/name/.* | ||
const EXPORTS_PATTERN = /^((?:@[^/\\%]+\/)?[^./\\%][^/\\%]*)(\/.*)?$/; | ||
function resolveExports(nmPath, request, absoluteRequest) { | ||
function resolveExports(nmPath, request) { | ||
// The implementation's behavior is meant to mirror resolution in ESM. | ||
if (!absoluteRequest) { | ||
const [, name, expansion = ''] = | ||
StringPrototypeMatch(request, EXPORTS_PATTERN) || []; | ||
if (!name) { | ||
return path.resolve(nmPath, request); | ||
} | ||
|
||
const basePath = path.resolve(nmPath, name); | ||
return applyExports(basePath, expansion); | ||
const [, name, expansion = ''] = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. using array destructuring means it will break if Symbol.iterator is deleted off of Array.prototype. this one has a lot of places in the codebase to fix, tho, so it’s probably fine to skip for now. |
||
StringPrototypeMatch(request, EXPORTS_PATTERN) || []; | ||
if (!name) { | ||
return false; | ||
} | ||
|
||
return path.resolve(nmPath, request); | ||
const basePath = path.resolve(nmPath, name); | ||
const fromExports = applyExports(basePath, expansion); | ||
if (fromExports) { | ||
return tryFile(fromExports, false); | ||
} | ||
return fromExports; | ||
} | ||
|
||
function isArrayIndex(p) { | ||
|
@@ -636,6 +600,7 @@ function resolveExportsTarget(baseUrl, target, subpath, mappingKey) { | |
StringPrototypeSlice(baseUrl.pathname, 0, -1), mappingKey, subpath, target); | ||
} | ||
|
||
const trailingSlashRegex = /(?:^|\/)\.?\.$/; | ||
Module._findPath = function(request, paths, isMain) { | ||
const absoluteRequest = path.isAbsolute(request); | ||
if (absoluteRequest) { | ||
|
@@ -654,15 +619,26 @@ Module._findPath = function(request, paths, isMain) { | |
let trailingSlash = request.length > 0 && | ||
request.charCodeAt(request.length - 1) === CHAR_FORWARD_SLASH; | ||
if (!trailingSlash) { | ||
trailingSlash = /(?:^|\/)\.?\.$/.test(request); | ||
trailingSlash = RegExpPrototypeTest(trailingSlashRegex, request); | ||
} | ||
|
||
// For each path | ||
for (let i = 0; i < paths.length; i++) { | ||
// Don't search further if path doesn't exist | ||
const curPath = paths[i]; | ||
if (curPath && stat(curPath) < 1) continue; | ||
const basePath = resolveExports(curPath, request, absoluteRequest); | ||
|
||
if (!absoluteRequest) { | ||
const exportsResolved = resolveExports(curPath, request); | ||
// Undefined means not found, false means no exports | ||
if (exportsResolved === undefined) | ||
break; | ||
if (exportsResolved) { | ||
return exportsResolved; | ||
} | ||
} | ||
|
||
const basePath = path.resolve(curPath, request); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. will There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, although there are a lot of calls to |
||
let filename; | ||
|
||
const rc = stat(basePath); | ||
|
@@ -1005,7 +981,7 @@ Module._resolveFilename = function(request, parent, isMain, options) { | |
} | ||
|
||
if (parent && parent.filename) { | ||
const filename = trySelf(parent.filename, isMain, request); | ||
const filename = trySelf(parent.filename, request); | ||
if (filename) { | ||
const cacheKey = request + '\x00' + | ||
(paths.length === 1 ? paths[0] : paths.join('\x00')); | ||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Uh oh!
There was an error while loading. Please reload this page.