@@ -34,6 +34,7 @@ const { |
34 | 34 | ObjectKeys, |
35 | 35 | ObjectPrototypeHasOwnProperty, |
36 | 36 | ReflectSet, |
| 37 | + RegExpPrototypeTest, |
37 | 38 | SafeMap, |
38 | 39 | String, |
39 | 40 | StringPrototypeIndexOf, |
@@ -121,7 +122,7 @@ function enrichCJSError(err) { |
121 | 122 | after a comment block and/or after a variable definition. |
122 | 123 | */ |
123 | 124 | if (err.message.startsWith('Unexpected token \'export\'') || |
124 | | - (/^\s*import(?=[ {'"*])\s*(?![ (])/).test(lineWithErr)) { |
| 125 | + (RegExpPrototypeTest(/^\s*import(?=[ {'"*])\s*(?![ (])/, lineWithErr))) { |
125 | 126 | process.emitWarning( |
126 | 127 | 'To load an ES module, set "type": "module" in the package.json or use ' + |
127 | 128 | 'the .mjs extension.', |
@@ -348,10 +349,11 @@ const realpathCache = new Map(); |
348 | 349 | // absolute realpath. |
349 | 350 | function tryFile(requestPath, isMain) { |
350 | 351 | const rc = stat(requestPath); |
| 352 | + if (rc !== 0) return; |
351 | 353 | if (preserveSymlinks && !isMain) { |
352 | | - return rc === 0 && path.resolve(requestPath); |
| 354 | + return path.resolve(requestPath); |
353 | 355 | } |
354 | | - return rc === 0 && toRealPath(requestPath); |
| 356 | + return toRealPath(requestPath); |
355 | 357 | } |
356 | 358 |
|
357 | 359 | function toRealPath(requestPath) { |
@@ -388,52 +390,7 @@ function findLongestRegisteredExtension(filename) { |
388 | 390 | return '.js'; |
389 | 391 | } |
390 | 392 |
|
391 | | -function resolveBasePath(basePath, exts, isMain, trailingSlash, request) { |
392 | | - let filename; |
393 | | - |
394 | | - const rc = stat(basePath); |
395 | | - if (!trailingSlash) { |
396 | | - if (rc === 0) { // File. |
397 | | - if (!isMain) { |
398 | | - if (preserveSymlinks) { |
399 | | - filename = path.resolve(basePath); |
400 | | - } else { |
401 | | - filename = toRealPath(basePath); |
402 | | - } |
403 | | - } else if (preserveSymlinksMain) { |
404 | | - // For the main module, we use the preserveSymlinksMain flag instead |
405 | | - // mainly for backward compatibility, as the preserveSymlinks flag |
406 | | - // historically has not applied to the main module. Most likely this |
407 | | - // was intended to keep .bin/ binaries working, as following those |
408 | | - // symlinks is usually required for the imports in the corresponding |
409 | | - // files to resolve; that said, in some use cases following symlinks |
410 | | - // causes bigger problems which is why the preserveSymlinksMain option |
411 | | - // is needed. |
412 | | - filename = path.resolve(basePath); |
413 | | - } else { |
414 | | - filename = toRealPath(basePath); |
415 | | - } |
416 | | - } |
417 | | - |
418 | | - if (!filename) { |
419 | | - // Try it with each of the extensions |
420 | | - if (exts === undefined) |
421 | | - exts = ObjectKeys(Module._extensions); |
422 | | - filename = tryExtensions(basePath, exts, isMain); |
423 | | - } |
424 | | - } |
425 | | - |
426 | | - if (!filename && rc === 1) { // Directory. |
427 | | - // try it with each of the extensions at "index" |
428 | | - if (exts === undefined) |
429 | | - exts = ObjectKeys(Module._extensions); |
430 | | - filename = tryPackage(basePath, exts, isMain, request); |
431 | | - } |
432 | | - |
433 | | - return filename; |
434 | | -} |
435 | | - |
436 | | -function trySelf(parentPath, isMain, request) { |
| 393 | +function trySelf(parentPath, request) { |
437 | 394 | const { data: pkg, path: basePath } = readPackageScope(parentPath) || {}; |
438 | 395 | if (!pkg || pkg.exports === undefined) return false; |
439 | 396 | if (typeof pkg.name !== 'string') return false; |
@@ -447,20 +404,11 @@ function trySelf(parentPath, isMain, request) { |
447 | 404 | return false; |
448 | 405 | } |
449 | 406 |
|
450 | | - const exts = ObjectKeys(Module._extensions); |
451 | 407 | const fromExports = applyExports(basePath, expansion); |
452 | | - // Use exports |
453 | 408 | if (fromExports) { |
454 | | - let trailingSlash = request.length > 0 && |
455 | | - request.charCodeAt(request.length - 1) === CHAR_FORWARD_SLASH; |
456 | | - if (!trailingSlash) { |
457 | | - trailingSlash = /(?:^|\/)\.?\.$/.test(request); |
458 | | - } |
459 | | - return resolveBasePath(fromExports, exts, isMain, trailingSlash, request); |
460 | | - } else { |
461 | | - // Use main field |
462 | | - return tryPackage(basePath, exts, isMain, request); |
| 409 | + return tryFile(fromExports, false); |
463 | 410 | } |
| 411 | + assert(fromExports !== false); |
464 | 412 | } |
465 | 413 |
|
466 | 414 | function isConditionalDotExportSugar(exports, basePath) { |
@@ -492,7 +440,7 @@ function applyExports(basePath, expansion) { |
492 | 440 |
|
493 | 441 | let pkgExports = readPackageExports(basePath); |
494 | 442 | if (pkgExports === undefined || pkgExports === null) |
495 | | - return path.resolve(basePath, mappingKey); |
| 443 | + return false; |
496 | 444 |
|
497 | 445 | if (isConditionalDotExportSugar(pkgExports, basePath)) |
498 | 446 | pkgExports = { '.': pkgExports }; |
@@ -516,8 +464,24 @@ function applyExports(basePath, expansion) { |
516 | 464 | if (dirMatch !== '') { |
517 | 465 | const mapping = pkgExports[dirMatch]; |
518 | 466 | const subpath = StringPrototypeSlice(mappingKey, dirMatch.length); |
519 | | - return resolveExportsTarget(pathToFileURL(basePath + '/'), mapping, |
520 | | - subpath, mappingKey); |
| 467 | + const resolved = resolveExportsTarget(pathToFileURL(basePath + '/'), |
| 468 | + mapping, subpath, mappingKey); |
| 469 | + // Extension searching for folder exports only |
| 470 | + const rc = stat(resolved); |
| 471 | + if (rc === 0) return resolved; |
| 472 | + if (!(RegExpPrototypeTest(trailingSlashRegex, resolved))) { |
| 473 | + const exts = ObjectKeys(Module._extensions); |
| 474 | + const filename = tryExtensions(resolved, exts, false); |
| 475 | + if (filename) return filename; |
| 476 | + } |
| 477 | + if (rc === 1) { |
| 478 | + const exts = ObjectKeys(Module._extensions); |
| 479 | + const filename = tryPackage(resolved, exts, false, |
| 480 | + basePath + expansion); |
| 481 | + if (filename) return filename; |
| 482 | + } |
| 483 | + // Undefined means not found |
| 484 | + return; |
521 | 485 | } |
522 | 486 | } |
523 | 487 |
|
@@ -528,20 +492,20 @@ function applyExports(basePath, expansion) { |
528 | 492 | // 1. name/.* |
529 | 493 | // 2. @scope/name/.* |
530 | 494 | const EXPORTS_PATTERN = /^((?:@[^/\\%]+\/)?[^./\\%][^/\\%]*)(\/.*)?$/; |
531 | | -function resolveExports(nmPath, request, absoluteRequest) { |
| 495 | +function resolveExports(nmPath, request) { |
532 | 496 | // The implementation's behavior is meant to mirror resolution in ESM. |
533 | | - if (!absoluteRequest) { |
534 | | - const [, name, expansion = ''] = |
535 | | - StringPrototypeMatch(request, EXPORTS_PATTERN) || []; |
536 | | - if (!name) { |
537 | | - return path.resolve(nmPath, request); |
538 | | - } |
539 | | - |
540 | | - const basePath = path.resolve(nmPath, name); |
541 | | - return applyExports(basePath, expansion); |
| 497 | + const [, name, expansion = ''] = |
| 498 | + StringPrototypeMatch(request, EXPORTS_PATTERN) || []; |
| 499 | + if (!name) { |
| 500 | + return false; |
542 | 501 | } |
543 | 502 |
|
544 | | - return path.resolve(nmPath, request); |
| 503 | + const basePath = path.resolve(nmPath, name); |
| 504 | + const fromExports = applyExports(basePath, expansion); |
| 505 | + if (fromExports) { |
| 506 | + return tryFile(fromExports, false); |
| 507 | + } |
| 508 | + return fromExports; |
545 | 509 | } |
546 | 510 |
|
547 | 511 | function isArrayIndex(p) { |
@@ -632,6 +596,7 @@ function resolveExportsTarget(baseUrl, target, subpath, mappingKey) { |
632 | 596 | StringPrototypeSlice(baseUrl.pathname, 0, -1), mappingKey, subpath, target); |
633 | 597 | } |
634 | 598 |
|
| 599 | +const trailingSlashRegex = /(?:^|\/)\.?\.$/; |
635 | 600 | Module._findPath = function(request, paths, isMain) { |
636 | 601 | const absoluteRequest = path.isAbsolute(request); |
637 | 602 | if (absoluteRequest) { |
@@ -650,15 +615,26 @@ Module._findPath = function(request, paths, isMain) { |
650 | 615 | let trailingSlash = request.length > 0 && |
651 | 616 | request.charCodeAt(request.length - 1) === CHAR_FORWARD_SLASH; |
652 | 617 | if (!trailingSlash) { |
653 | | - trailingSlash = /(?:^|\/)\.?\.$/.test(request); |
| 618 | + trailingSlash = RegExpPrototypeTest(trailingSlashRegex, request); |
654 | 619 | } |
655 | 620 |
|
656 | 621 | // For each path |
657 | 622 | for (let i = 0; i < paths.length; i++) { |
658 | 623 | // Don't search further if path doesn't exist |
659 | 624 | const curPath = paths[i]; |
660 | 625 | if (curPath && stat(curPath) < 1) continue; |
661 | | - const basePath = resolveExports(curPath, request, absoluteRequest); |
| 626 | + |
| 627 | + if (!absoluteRequest) { |
| 628 | + const exportsResolved = resolveExports(curPath, request); |
| 629 | + // Undefined means not found, false means no exports |
| 630 | + if (exportsResolved === undefined) |
| 631 | + break; |
| 632 | + if (exportsResolved) { |
| 633 | + return exportsResolved; |
| 634 | + } |
| 635 | + } |
| 636 | + |
| 637 | + const basePath = path.resolve(curPath, request); |
662 | 638 | let filename; |
663 | 639 |
|
664 | 640 | const rc = stat(basePath); |
@@ -950,7 +926,7 @@ Module._resolveFilename = function(request, parent, isMain, options) { |
950 | 926 | } |
951 | 927 |
|
952 | 928 | if (parent && parent.filename) { |
953 | | - const filename = trySelf(parent.filename, isMain, request); |
| 929 | + const filename = trySelf(parent.filename, request); |
954 | 930 | if (filename) { |
955 | 931 | const cacheKey = request + '\x00' + |
956 | 932 | (paths.length === 1 ? paths[0] : paths.join('\x00')); |
|