Skip to content

Commit d422e58

Browse files
dygabodanielleadams
authored andcommitted
loader: fix package resolution for edge case
this commit solves a regression introduced with PR-40980. if a resolve call results in a script with .mjs extension the is automatically set to . This avoids the case where an additional in the same directory as the .mjs file would declare the to commonjs PR-URL: #41218 Refs: #40980 Refs: yargs/yargs#2068 Reviewed-By: Guy Bedford <guybedford@gmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Backport-PR-URL: #41752 Reviewed-By: Danielle Adams <adamzdanielle@gmail.com> Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com> Reviewed-By: Gerhard Stöbich <deb2001-github@yahoo.de>
1 parent 694dc12 commit d422e58

4 files changed

Lines changed: 240 additions & 176 deletions

File tree

‎lib/internal/modules/esm/get_format.js‎

Lines changed: 42 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ const legacyExtensionFormatMap = {
3232
'.node': 'commonjs'
3333
};
3434

35+
letexperimentalSpecifierResolutionWarned=false;
36+
3537
if(experimentalWasmModules)
3638
extensionFormatMap['.wasm']=legacyExtensionFormatMap['.wasm']='wasm';
3739

@@ -53,41 +55,57 @@ const protocolHandlers = ObjectAssign(ObjectCreate(null), {
5355

5456
returnformat;
5557
},
56-
'file:'(parsed,url){
57-
constext=extname(parsed.pathname);
58-
letformat;
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-
thrownewERR_UNKNOWN_FILE_EXTENSION(ext,fileURLToPath(url));
73-
}
74-
}
75-
76-
returnformat||null;
77-
},
58+
'file:': getFileProtocolModuleFormat,
7859
'node:'(){return'builtin';},
7960
});
8061

81-
functiondefaultGetFormat(url,context){
62+
functiongetLegacyExtensionFormat(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+
returnlegacyExtensionFormatMap[ext];
73+
}
74+
75+
functiongetFileProtocolModuleFormat(url,ignoreErrors){
76+
constext=extname(url.pathname);
77+
if(ext==='.js'){
78+
returngetPackageType(url)==='module' ? 'module' : 'commonjs';
79+
}
80+
81+
constformat=extensionFormatMap[ext];
82+
if(format)returnformat;
83+
if(experimentalSpecifierResolution!=='node'){
84+
// Explicit undefined return indicates load hook should rerun format check
85+
if(ignoreErrors)
86+
returnundefined;
87+
thrownewERR_UNKNOWN_FILE_EXTENSION(ext,fileURLToPath(url));
88+
}
89+
returngetLegacyExtensionFormat(ext)??null;
90+
}
91+
92+
functiondefaultGetFormatWithoutErrors(url,context){
8293
constparsed=newURL(url);
94+
if(!ObjectPrototypeHasOwnProperty(protocolHandlers,parsed.protocol))
95+
returnnull;
96+
returnprotocolHandlers[parsed.protocol](parsed,true);
97+
}
8398

99+
functiondefaultGetFormat(url,context){
100+
constparsed=newURL(url);
84101
returnObjectPrototypeHasOwnProperty(protocolHandlers,parsed.protocol) ?
85-
protocolHandlers[parsed.protocol](parsed,url) :
102+
protocolHandlers[parsed.protocol](parsed,false) :
86103
null;
87104
}
88105

89106
module.exports={
90107
defaultGetFormat,
108+
defaultGetFormatWithoutErrors,
91109
extensionFormatMap,
92110
legacyExtensionFormatMap,
93111
};

‎lib/internal/modules/esm/load.js‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
const{ defaultGetFormat }=require('internal/modules/esm/get_format');
44
const{ defaultGetSource }=require('internal/modules/esm/get_source');
5-
const{ translators }=require('internal/modules/esm/translators');
65
const{ validateAssertions }=require('internal/modules/esm/assert');
76

87
/**
@@ -18,7 +17,7 @@ async function defaultLoad(url, context) {
1817
}=context;
1918
const{ importAssertions }=context;
2019

21-
if(!format||!translators.has(format)){
20+
if(format==null){
2221
format=defaultGetFormat(url);
2322
}
2423

‎lib/internal/modules/esm/resolve.js‎

Lines changed: 49 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ function emitTrailingSlashPatternDeprecation(match, pjsonUrl, isExports, base) {
131131
* @returns {void}
132132
*/
133133
functionemitLegacyIndexDeprecation(url,packageJSONUrl,base,main){
134-
constformat=defaultGetFormat(url);
134+
constformat=defaultGetFormatWithoutErrors(url);
135135
if(format!=='module')
136136
return;
137137
constpath=fileURLToPath(url);
@@ -488,22 +488,6 @@ const patternRegEx = /\*/g;
488488
functionresolvePackageTargetString(
489489
target,subpath,match,packageJSONUrl,base,pattern,internal,conditions){
490490

491-
constcomposeResult=(resolved)=>{
492-
letformat;
493-
try{
494-
format=getPackageType(resolved);
495-
}catch(err){
496-
if(err.code==='ERR_INVALID_FILE_URL_PATH'){
497-
constinvalidModuleErr=newERR_INVALID_MODULE_SPECIFIER(
498-
resolved,'must not include encoded "/" or "\\" characters',base);
499-
invalidModuleErr.cause=err;
500-
throwinvalidModuleErr;
501-
}
502-
throwerr;
503-
}
504-
return{ resolved, ...(format!=='none')&&{ format }};
505-
};
506-
507491
if(subpath!==''&&!pattern&&target[target.length-1]!=='/')
508492
throwInvalidPackageTarget(match,target,packageJSONUrl,internal,base);
509493

@@ -536,18 +520,22 @@ function resolvePackageTargetString(
536520
if(!StringPrototypeStartsWith(resolvedPath,packagePath))
537521
throwInvalidPackageTarget(match,target,packageJSONUrl,internal,base);
538522

539-
if(subpath==='')returncomposeResult(resolved);
523+
if(subpath==='')returnresolved;
540524

541525
if(RegExpPrototypeTest(invalidSegmentRegEx,subpath))
542526
throwInvalidSubpath(match+subpath,packageJSONUrl,internal,base);
543527

544528
if(pattern){
545-
returncomposeResult(newURL(RegExpPrototypeSymbolReplace(patternRegEx,
546-
resolved.href,
547-
()=>subpath)));
529+
returnnewURL(
530+
RegExpPrototypeSymbolReplace(
531+
patternRegEx,
532+
resolved.href,
533+
()=>subpath
534+
)
535+
);
548536
}
549537

550-
returncomposeResult(newURL(subpath,resolved));
538+
returnnewURL(subpath,resolved);
551539
}
552540

553541
/**
@@ -673,15 +661,15 @@ function packageExportsResolve(
673661
!StringPrototypeIncludes(packageSubpath,'*')&&
674662
!StringPrototypeEndsWith(packageSubpath,'/')){
675663
consttarget=exports[packageSubpath];
676-
constresolveResult=resolvePackageTarget(
664+
constresolved=resolvePackageTarget(
677665
packageJSONUrl,target,'',packageSubpath,base,false,false,conditions
678666
);
679667

680-
if(resolveResult==null){
668+
if(resolved==null){
681669
throwExportsNotFound(packageSubpath,packageJSONUrl,base);
682670
}
683671

684-
return{...resolveResult,exact: true};
672+
return{resolved,exact: true};
685673
}
686674

687675
letbestMatch='';
@@ -717,7 +705,7 @@ function packageExportsResolve(
717705
if(bestMatch){
718706
consttarget=exports[bestMatch];
719707
constpattern=StringPrototypeIncludes(bestMatch,'*');
720-
constresolveResult=resolvePackageTarget(
708+
constresolved=resolvePackageTarget(
721709
packageJSONUrl,
722710
target,
723711
bestMatchSubpath,
@@ -727,15 +715,15 @@ function packageExportsResolve(
727715
false,
728716
conditions);
729717

730-
if(resolveResult==null){
718+
if(resolved==null){
731719
throwExportsNotFound(packageSubpath,packageJSONUrl,base);
732720
}
733721

734722
if(!pattern){
735723
emitFolderMapDeprecation(bestMatch,packageJSONUrl,true,base);
736724
}
737725

738-
return{...resolveResult,exact: pattern};
726+
return{resolved,exact: pattern};
739727
}
740728

741729
throwExportsNotFound(packageSubpath,packageJSONUrl,base);
@@ -775,11 +763,11 @@ function packageImportsResolve(name, base, conditions) {
775763
if(ObjectPrototypeHasOwnProperty(imports,name)&&
776764
!StringPrototypeIncludes(name,'*')&&
777765
!StringPrototypeEndsWith(name,'/')){
778-
constresolveResult=resolvePackageTarget(
766+
constresolved=resolvePackageTarget(
779767
packageJSONUrl,imports[name],'',name,base,false,true,conditions
780768
);
781-
if(resolveResult!=null){
782-
return{resolved: resolveResult.resolved,exact: true};
769+
if(resolved!=null){
770+
return{ resolved,exact: true};
783771
}
784772
}else{
785773
letbestMatch='';
@@ -812,15 +800,15 @@ function packageImportsResolve(name, base, conditions) {
812800
if(bestMatch){
813801
consttarget=imports[bestMatch];
814802
constpattern=StringPrototypeIncludes(bestMatch,'*');
815-
constresolveResult=resolvePackageTarget(
803+
constresolved=resolvePackageTarget(
816804
packageJSONUrl,target,
817805
bestMatchSubpath,bestMatch,
818806
base,pattern,true,
819807
conditions);
820-
if(resolveResult!==null){
808+
if(resolved!==null){
821809
if(!pattern)
822810
emitFolderMapDeprecation(bestMatch,packageJSONUrl,false,base);
823-
return{resolved: resolveResult.resolved,exact: pattern};
811+
return{ resolved,exact: pattern};
824812
}
825813
}
826814
}
@@ -880,7 +868,7 @@ function parsePackageName(specifier, base) {
880868
* @param {string} specifier
881869
* @param {string | URL | undefined} base
882870
* @param {Set<string>} conditions
883-
* @returns {resolved: URL, format? : string}
871+
* @returns {URL}
884872
*/
885873
functionpackageResolve(specifier,base,conditions){
886874
if(NativeModule.canBeRequiredByUsers(specifier))
@@ -896,7 +884,8 @@ function packageResolve(specifier, base, conditions) {
896884
if(packageConfig.name===packageName&&
897885
packageConfig.exports!==undefined&&packageConfig.exports!==null){
898886
returnpackageExportsResolve(
899-
packageJSONUrl,packageSubpath,packageConfig,base,conditions);
887+
packageJSONUrl,packageSubpath,packageConfig,base,conditions
888+
).resolved;
900889
}
901890
}
902891

@@ -920,24 +909,19 @@ function packageResolve(specifier, base, conditions) {
920909
constpackageConfig=getPackageConfig(packageJSONPath,specifier,base);
921910
if(packageConfig.exports!==undefined&&packageConfig.exports!==null){
922911
returnpackageExportsResolve(
923-
packageJSONUrl,packageSubpath,packageConfig,base,conditions);
912+
packageJSONUrl,packageSubpath,packageConfig,base,conditions
913+
).resolved;
924914
}
925915

926916
if(packageSubpath==='.'){
927-
return{
928-
resolved: legacyMainResolve(
929-
packageJSONUrl,
930-
packageConfig,
931-
base),
932-
...(packageConfig.type!=='none')&&{format: packageConfig.type}
933-
};
917+
returnlegacyMainResolve(
918+
packageJSONUrl,
919+
packageConfig,
920+
base
921+
);
934922
}
935923

936-
return{
937-
resolved: newURL(packageSubpath,packageJSONUrl),
938-
...(packageConfig.type!=='none')&&{format: packageConfig.type}
939-
};
940-
924+
returnnewURL(packageSubpath,packageJSONUrl);
941925
// Cross-platform root check.
942926
}while(packageJSONPath.length!==lastPath.length);
943927

@@ -981,7 +965,6 @@ function moduleResolve(specifier, base, conditions, preserveSymlinks) {
981965
// Order swapped from spec for minor perf gain.
982966
// Ok since relative URLs cannot parse as URLs.
983967
letresolved;
984-
letformat;
985968
if(shouldBeTreatedAsRelativeOrAbsolutePath(specifier)){
986969
resolved=newURL(specifier,base);
987970
}elseif(specifier[0]==='#'){
@@ -990,15 +973,12 @@ function moduleResolve(specifier, base, conditions, preserveSymlinks) {
990973
try{
991974
resolved=newURL(specifier);
992975
}catch{
993-
({resolved, format }=packageResolve(specifier,base,conditions));
976+
resolved=packageResolve(specifier,base,conditions);
994977
}
995978
}
996979
if(resolved.protocol!=='file:')
997980
returnresolved;
998-
return{
999-
url: finalizeResolution(resolved,base,preserveSymlinks),
1000-
...(format!=null)&&{ format }
1001-
};
981+
returnfinalizeResolution(resolved,base,preserveSymlinks);
1002982
}
1003983

1004984
/**
@@ -1047,6 +1027,13 @@ function resolveAsCommonJS(specifier, parentURL) {
10471027
}
10481028
}
10491029

1030+
functionthrowIfUnsupportedURLProtocol(url){
1031+
if(url.protocol!=='file:'&&url.protocol!=='data:'&&
1032+
url.protocol!=='node:'){
1033+
thrownewERR_UNSUPPORTED_ESM_URL_SCHEME(url);
1034+
}
1035+
}
1036+
10501037
functiondefaultResolve(specifier,context={},defaultResolveUnused){
10511038
let{ parentURL, conditions }=context;
10521039
if(parentURL&&policy?.manifest){
@@ -1087,15 +1074,9 @@ function defaultResolve(specifier, context = {}, defaultResolveUnused) {
10871074

10881075
conditions=getConditionsSet(conditions);
10891076
leturl;
1090-
letformat;
10911077
try{
1092-
({ url, format }=
1093-
moduleResolve(
1094-
specifier,
1095-
parentURL,
1096-
conditions,
1097-
isMain ? preserveSymlinksMain : preserveSymlinks
1098-
));
1078+
url=moduleResolve(specifier,parentURL,conditions,
1079+
isMain ? preserveSymlinksMain : preserveSymlinks);
10991080
}catch(error){
11001081
// Try to give the user a hint of what would have been the
11011082
// resolved CommonJS module
@@ -1119,13 +1100,11 @@ function defaultResolve(specifier, context = {}, defaultResolveUnused) {
11191100
throwerror;
11201101
}
11211102

1122-
if(url.protocol!=='file:'&&url.protocol!=='data:'&&
1123-
url.protocol!=='node:')
1124-
thrownewERR_UNSUPPORTED_ESM_URL_SCHEME(url);
1103+
throwIfUnsupportedURLProtocol(url);
11251104

11261105
return{
11271106
url: `${url}`,
1128-
...(format!=null)&&{ format }
1107+
format: defaultGetFormatWithoutErrors(url),
11291108
};
11301109
}
11311110

@@ -1140,4 +1119,6 @@ module.exports = {
11401119
};
11411120

11421121
// cycle
1143-
const{ defaultGetFormat }=require('internal/modules/esm/get_format');
1122+
const{
1123+
defaultGetFormatWithoutErrors,
1124+
}=require('internal/modules/esm/get_format');

0 commit comments

Comments
 (0)