GeezerVison is showing github.com — loader: fix package resolution for edge case · nodejs/node@68fd2ac
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: lib/internal/modules/cjs/loader.js +2-2Lines changed: 2 additions & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -457,7 +457,7 @@ function trySelf(parentPath, request) { | 457 | 457 | try { | 458 | 458 | return finalizeEsmResolution(packageExportsResolve( | 459 | 459 | pathToFileURL(pkgPath + '/package.json'), expansion, pkg, | 460 | | - pathToFileURL(parentPath), cjsConditions).resolved, parentPath, pkgPath); | | 460 | + pathToFileURL(parentPath), cjsConditions), parentPath, pkgPath); | 461 | 461 | } catch (e) { | 462 | 462 | if (e.code === 'ERR_MODULE_NOT_FOUND') | 463 | 463 | throw createEsmNotFoundErr(request, pkgPath + '/package.json'); | @@ -481,7 +481,7 @@ function resolveExports(nmPath, request) { | 481 | 481 | try { | 482 | 482 | return finalizeEsmResolution(packageExportsResolve( | 483 | 483 | pathToFileURL(pkgPath + '/package.json'), '.' + expansion, pkg, null, | 484 | | - cjsConditions).resolved, null, pkgPath); | | 484 | + cjsConditions), null, pkgPath); | 485 | 485 | } catch (e) { | 486 | 486 | if (e.code === 'ERR_MODULE_NOT_FOUND') | 487 | 487 | throw createEsmNotFoundErr(request, pkgPath + '/package.json'); | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/modules/esm/get_format.js +42-24Lines changed: 42 additions & 24 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -32,6 +32,8 @@ const legacyExtensionFormatMap = { | 32 | 32 | '.node': 'commonjs' | 33 | 33 | }; | 34 | 34 |
| | 35 | +let experimentalSpecifierResolutionWarned = false; | | 36 | + | 35 | 37 | if (experimentalWasmModules) | 36 | 38 | extensionFormatMap['.wasm'] = legacyExtensionFormatMap['.wasm'] = 'wasm'; | 37 | 39 |
| @@ -53,41 +55,57 @@ const protocolHandlers = ObjectAssign(ObjectCreate(null), { | 53 | 55 |
| 54 | 56 | return format; | 55 | 57 | }, | 56 | | - 'file:'(parsed, url) { | 57 | | - const ext = extname(parsed.pathname); | 58 | | - let format; | 59 | | - | 60 | | - if (ext === '.js') { | 61 | | - format = getPackageType(parsed.href) === 'module' ? 'module' : 'commonjs'; | 62 | | - } else { | 63 | | - format = extensionFormatMap[ext]; | 64 | | - } | 65 | | - if (!format) { | 66 | | - if (experimentalSpecifierResolution === 'node') { | 67 | | - process.emitWarning( | 68 | | - 'The Node.js specifier resolution in ESM is experimental.', | 69 | | - 'ExperimentalWarning'); | 70 | | - format = legacyExtensionFormatMap[ext]; | 71 | | - } else { | 72 | | - throw new ERR_UNKNOWN_FILE_EXTENSION(ext, fileURLToPath(url)); | 73 | | - } | 74 | | - } | 75 | | - | 76 | | - return format || null; | 77 | | - }, | | 58 | + 'file:': getFileProtocolModuleFormat, | 78 | 59 | 'node:'() { return 'builtin'; }, | 79 | 60 | }); | 80 | 61 |
| 81 | | -function defaultGetFormat(url, context) { | | 62 | +function getLegacyExtensionFormat(ext) { | | 63 | + if ( | | 64 | + experimentalSpecifierResolution === 'node' && | | 65 | + !experimentalSpecifierResolutionWarned | | 66 | + ) { | | 67 | + process.emitWarning( | | 68 | + 'The Node.js specifier resolution in ESM is experimental.', | | 69 | + 'ExperimentalWarning'); | | 70 | + experimentalSpecifierResolutionWarned = true; | | 71 | + } | | 72 | + return legacyExtensionFormatMap[ext]; | | 73 | +} | | 74 | + | | 75 | +function getFileProtocolModuleFormat(url, ignoreErrors) { | | 76 | + const ext = extname(url.pathname); | | 77 | + if (ext === '.js') { | | 78 | + return getPackageType(url) === 'module' ? 'module' : 'commonjs'; | | 79 | + } | | 80 | + | | 81 | + const format = extensionFormatMap[ext]; | | 82 | + if (format) return format; | | 83 | + if (experimentalSpecifierResolution !== 'node') { | | 84 | + // Explicit undefined return indicates load hook should rerun format check | | 85 | + if (ignoreErrors) | | 86 | + return undefined; | | 87 | + throw new ERR_UNKNOWN_FILE_EXTENSION(ext, fileURLToPath(url)); | | 88 | + } | | 89 | + return getLegacyExtensionFormat(ext) ?? null; | | 90 | +} | | 91 | + | | 92 | +function defaultGetFormatWithoutErrors(url, context) { | 82 | 93 | const parsed = new URL(url); | | 94 | + if (!ObjectPrototypeHasOwnProperty(protocolHandlers, parsed.protocol)) | | 95 | + return null; | | 96 | + return protocolHandlers[parsed.protocol](parsed, true); | | 97 | +} | 83 | 98 |
| | 99 | +function defaultGetFormat(url, context) { | | 100 | + const parsed = new URL(url); | 84 | 101 | return ObjectPrototypeHasOwnProperty(protocolHandlers, parsed.protocol) ? | 85 | | - protocolHandlers[parsed.protocol](parsed, url) : | | 102 | + protocolHandlers[parsed.protocol](parsed, false) : | 86 | 103 | null; | 87 | 104 | } | 88 | 105 |
| 89 | 106 | module.exports = { | 90 | 107 | defaultGetFormat, | | 108 | + defaultGetFormatWithoutErrors, | 91 | 109 | extensionFormatMap, | 92 | 110 | legacyExtensionFormatMap, | 93 | 111 | }; |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/modules/esm/load.js +1-2Lines changed: 1 addition & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 2 | 2 |
| 3 | 3 | const { defaultGetFormat } = require('internal/modules/esm/get_format'); | 4 | 4 | const { defaultGetSource } = require('internal/modules/esm/get_source'); | 5 | | -const { translators } = require('internal/modules/esm/translators'); | 6 | 5 | const { validateAssertions } = require('internal/modules/esm/assert'); | 7 | 6 |
| 8 | 7 | /** | @@ -18,7 +17,7 @@ async function defaultLoad(url, context) { | 18 | 17 | } = context; | 19 | 18 | const { importAssertions } = context; | 20 | 19 |
| 21 | | - if (!format || !translators.has(format)) { | | 20 | + if (format == null) { | 22 | 21 | format = defaultGetFormat(url); | 23 | 22 | } | 24 | 23 |
| |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/modules/esm/resolve.js +40-59Lines changed: 40 additions & 59 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -107,7 +107,7 @@ function emitTrailingSlashPatternDeprecation(match, pjsonUrl, base) { | 107 | 107 | * @returns {void} | 108 | 108 | */ | 109 | 109 | function emitLegacyIndexDeprecation(url, packageJSONUrl, base, main) { | 110 | | - const format = defaultGetFormat(url); | | 110 | + const format = defaultGetFormatWithoutErrors(url); | 111 | 111 | if (format !== 'module') | 112 | 112 | return; | 113 | 113 | const path = fileURLToPath(url); | @@ -464,22 +464,6 @@ const patternRegEx = /\*/g; | 464 | 464 | function resolvePackageTargetString( | 465 | 465 | target, subpath, match, packageJSONUrl, base, pattern, internal, conditions) { | 466 | 466 |
| 467 | | - const composeResult = (resolved) => { | 468 | | - let format; | 469 | | - try { | 470 | | - format = getPackageType(resolved); | 471 | | - } catch (err) { | 472 | | - if (err.code === 'ERR_INVALID_FILE_URL_PATH') { | 473 | | - const invalidModuleErr = new ERR_INVALID_MODULE_SPECIFIER( | 474 | | - resolved, 'must not include encoded "/" or "\\" characters', base); | 475 | | - invalidModuleErr.cause = err; | 476 | | - throw invalidModuleErr; | 477 | | - } | 478 | | - throw err; | 479 | | - } | 480 | | - return { resolved, ...(format !== 'none') && { format } }; | 481 | | - }; | 482 | | - | 483 | 467 | if (subpath !== '' && !pattern && target[target.length - 1] !== '/') | 484 | 468 | throwInvalidPackageTarget(match, target, packageJSONUrl, internal, base); | 485 | 469 |
| @@ -512,7 +496,7 @@ function resolvePackageTargetString( | 512 | 496 | if (!StringPrototypeStartsWith(resolvedPath, packagePath)) | 513 | 497 | throwInvalidPackageTarget(match, target, packageJSONUrl, internal, base); | 514 | 498 |
| 515 | | - if (subpath === '') return composeResult(resolved); | | 499 | + if (subpath === '') return resolved; | 516 | 500 |
| 517 | 501 | if (RegExpPrototypeTest(invalidSegmentRegEx, subpath)) { | 518 | 502 | const request = pattern ? | @@ -521,12 +505,16 @@ function resolvePackageTargetString( | 521 | 505 | } | 522 | 506 |
| 523 | 507 | if (pattern) { | 524 | | - return composeResult(new URL(RegExpPrototypeSymbolReplace(patternRegEx, | 525 | | - resolved.href, | 526 | | - () => subpath))); | | 508 | + return new URL( | | 509 | + RegExpPrototypeSymbolReplace( | | 510 | + patternRegEx, | | 511 | + resolved.href, | | 512 | + () => subpath | | 513 | + ) | | 514 | + ); | 527 | 515 | } | 528 | 516 |
| 529 | | - return composeResult(new URL(subpath, resolved)); | | 517 | + return new URL(subpath, resolved); | 530 | 518 | } | 531 | 519 |
| 532 | 520 | /** | @@ -753,7 +741,7 @@ function packageImportsResolve(name, base, conditions) { | 753 | 741 | packageJSONUrl, imports[name], '', name, base, false, true, conditions | 754 | 742 | ); | 755 | 743 | if (resolveResult != null) { | 756 | | - return resolveResult.resolved; | | 744 | + return resolveResult; | 757 | 745 | } | 758 | 746 | } else { | 759 | 747 | let bestMatch = ''; | @@ -785,7 +773,7 @@ function packageImportsResolve(name, base, conditions) { | 785 | 773 | bestMatch, base, true, | 786 | 774 | true, conditions); | 787 | 775 | if (resolveResult != null) { | 788 | | - return resolveResult.resolved; | | 776 | + return resolveResult; | 789 | 777 | } | 790 | 778 | } | 791 | 779 | } | @@ -849,7 +837,7 @@ function parsePackageName(specifier, base) { | 849 | 837 | */ | 850 | 838 | function packageResolve(specifier, base, conditions) { | 851 | 839 | if (NativeModule.canBeRequiredByUsers(specifier)) | 852 | | - return { resolved: new URL('node:' + specifier) }; | | 840 | + return new URL('node:' + specifier); | 853 | 841 |
| 854 | 842 | const { packageName, packageSubpath, isScoped } = | 855 | 843 | parsePackageName(specifier, base); | @@ -888,19 +876,14 @@ function packageResolve(specifier, base, conditions) { | 888 | 876 | packageJSONUrl, packageSubpath, packageConfig, base, conditions); | 889 | 877 | } | 890 | 878 | if (packageSubpath === '.') { | 891 | | - return { | 892 | | - resolved: legacyMainResolve( | 893 | | - packageJSONUrl, | 894 | | - packageConfig, | 895 | | - base), | 896 | | - ...(packageConfig.type !== 'none') && { format: packageConfig.type } | 897 | | - }; | | 879 | + return legacyMainResolve( | | 880 | + packageJSONUrl, | | 881 | + packageConfig, | | 882 | + base | | 883 | + ); | 898 | 884 | } | 899 | 885 |
| 900 | | - return { | 901 | | - resolved: new URL(packageSubpath, packageJSONUrl), | 902 | | - ...(packageConfig.type !== 'none') && { format: packageConfig.type } | 903 | | - }; | | 886 | + return new URL(packageSubpath, packageJSONUrl); | 904 | 887 | // Cross-platform root check. | 905 | 888 | } while (packageJSONPath.length !== lastPath.length); | 906 | 889 |
| @@ -944,7 +927,6 @@ function moduleResolve(specifier, base, conditions, preserveSymlinks) { | 944 | 927 | // Order swapped from spec for minor perf gain. | 945 | 928 | // Ok since relative URLs cannot parse as URLs. | 946 | 929 | let resolved; | 947 | | - let format; | 948 | 930 | if (shouldBeTreatedAsRelativeOrAbsolutePath(specifier)) { | 949 | 931 | resolved = new URL(specifier, base); | 950 | 932 | } else if (specifier[0] === '#') { | @@ -953,19 +935,13 @@ function moduleResolve(specifier, base, conditions, preserveSymlinks) { | 953 | 935 | try { | 954 | 936 | resolved = new URL(specifier); | 955 | 937 | } catch { | 956 | | - ({ resolved, format } = packageResolve(specifier, base, conditions)); | | 938 | + resolved = packageResolve(specifier, base, conditions); | 957 | 939 | } | 958 | 940 | } | 959 | 941 | if (resolved.protocol !== 'file:') { | 960 | | - return { | 961 | | - url: resolved | 962 | | - }; | | 942 | + return resolved; | 963 | 943 | } | 964 | | - | 965 | | - return { | 966 | | - url: finalizeResolution(resolved, base, preserveSymlinks), | 967 | | - ...(format != null) && { format } | 968 | | - }; | | 944 | + return finalizeResolution(resolved, base, preserveSymlinks); | 969 | 945 | } | 970 | 946 |
| 971 | 947 | /** | @@ -1014,6 +990,13 @@ function resolveAsCommonJS(specifier, parentURL) { | 1014 | 990 | } | 1015 | 991 | } | 1016 | 992 |
| | 993 | +function throwIfUnsupportedURLProtocol(url) { | | 994 | + if (url.protocol !== 'file:' && url.protocol !== 'data:' && | | 995 | + url.protocol !== 'node:') { | | 996 | + throw new ERR_UNSUPPORTED_ESM_URL_SCHEME(url); | | 997 | + } | | 998 | +} | | 999 | + | 1017 | 1000 | function defaultResolve(specifier, context = {}, defaultResolveUnused) { | 1018 | 1001 | let { parentURL, conditions } = context; | 1019 | 1002 | if (parentURL && policy?.manifest) { | @@ -1054,15 +1037,13 @@ function defaultResolve(specifier, context = {}, defaultResolveUnused) { | 1054 | 1037 |
| 1055 | 1038 | conditions = getConditionsSet(conditions); | 1056 | 1039 | let url; | 1057 | | - let format; | 1058 | 1040 | try { | 1059 | | - ({ url, format } = | 1060 | | - moduleResolve( | 1061 | | - specifier, | 1062 | | - parentURL, | 1063 | | - conditions, | 1064 | | - isMain ? preserveSymlinksMain : preserveSymlinks | 1065 | | - )); | | 1041 | + url = moduleResolve( | | 1042 | + specifier, | | 1043 | + parentURL, | | 1044 | + conditions, | | 1045 | + isMain ? preserveSymlinksMain : preserveSymlinks | | 1046 | + ); | 1066 | 1047 | } catch (error) { | 1067 | 1048 | // Try to give the user a hint of what would have been the | 1068 | 1049 | // resolved CommonJS module | @@ -1086,13 +1067,11 @@ function defaultResolve(specifier, context = {}, defaultResolveUnused) { | 1086 | 1067 | throw error; | 1087 | 1068 | } | 1088 | 1069 |
| 1089 | | - if (url.protocol !== 'file:' && url.protocol !== 'data:' && | 1090 | | - url.protocol !== 'node:') | 1091 | | - throw new ERR_UNSUPPORTED_ESM_URL_SCHEME(url); | | 1070 | + throwIfUnsupportedURLProtocol(url); | 1092 | 1071 |
| 1093 | 1072 | return { | 1094 | 1073 | url: `${url}`, | 1095 | | - ...(format != null) && { format } | | 1074 | + format: defaultGetFormatWithoutErrors(url), | 1096 | 1075 | }; | 1097 | 1076 | } | 1098 | 1077 |
| @@ -1107,4 +1086,6 @@ module.exports = { | 1107 | 1086 | }; | 1108 | 1087 |
| 1109 | 1088 | // cycle | 1110 | | -const { defaultGetFormat } = require('internal/modules/esm/get_format'); | | 1089 | +const { | | 1090 | + defaultGetFormatWithoutErrors, | | 1091 | +} = require('internal/modules/esm/get_format'); |
|