@@ -4,38 +4,35 @@ import {
4
4
containsPath , createGetCanonicalFileName , Debug , directorySeparator , emptyArray , endsWith ,
5
5
ensurePathIsNonModuleName , ensureTrailingDirectorySeparator , every , ExportAssignment , Extension , extensionFromPath ,
6
6
fileExtensionIsOneOf , FileIncludeKind , firstDefined , flatMap , flatten , forEach , forEachAncestorDirectory ,
7
- GetCanonicalFileName , getDirectoryPath , getEmitModuleResolutionKind , getImpliedNodeFormatForFile ,
7
+ GetCanonicalFileName , getDirectoryPath , getEmitModuleResolutionKind ,
8
8
getModeForResolutionAtIndex , getModuleNameStringLiteralAt , getNodeModulePathParts , getNormalizedAbsolutePath ,
9
9
getOwnKeys , getPackageJsonTypesVersionsPaths , getPackageNameFromTypesPackageName , getPathsBasePath ,
10
10
getRelativePathFromDirectory , getRelativePathToDirectoryOrUrl , getSourceFileOfModule , getSupportedExtensions ,
11
11
getTextOfIdentifierOrLiteral , hasJSFileExtension , hasTSFileExtension , hostGetCanonicalFileName , Identifier ,
12
12
isAmbientModule , isApplicableVersionedTypesKey , isExternalModuleAugmentation , isExternalModuleNameRelative ,
13
13
isModuleBlock , isModuleDeclaration , isNonGlobalAmbientModule , isRootedDiskPath , isSourceFile , isString , JsxEmit ,
14
14
map , mapDefined , MapLike , matchPatternOrExact , min , ModuleDeclaration , ModuleKind , ModulePath ,
15
- ModuleResolutionHost , ModuleResolutionKind , ModuleSpecifierCache , ModuleSpecifierOptions ,
15
+ ModuleResolutionKind , ModuleSpecifierCache , ModuleSpecifierOptions ,
16
16
ModuleSpecifierResolutionHost , NodeFlags , NodeModulePathParts , normalizePath , Path , pathContainsNodeModules ,
17
17
pathIsBareSpecifier , pathIsRelative , PropertyAccessExpression , removeFileExtension , removeSuffix , resolvePath ,
18
18
ScriptKind , some , SourceFile , startsWith , startsWithDirectory , stringContains , StringLiteral , Symbol , SymbolFlags ,
19
- toPath , tryGetExtensionFromPath , tryParsePatterns , TypeChecker , UserPreferences , shouldAllowImportingTsExtension , ResolutionMode ,
19
+ toPath , tryGetExtensionFromPath , tryParsePatterns , TypeChecker , UserPreferences , shouldAllowImportingTsExtension , ResolutionMode , ModuleSpecifierEnding , getModuleSpecifierEndingPreference ,
20
20
} from "./_namespaces/ts" ;
21
21
22
22
// Used by importFixes, getEditsForFileRename, and declaration emit to synthesize import module specifiers.
23
23
24
24
const enum RelativePreference { Relative , NonRelative , Shortest , ExternalNonRelative }
25
- // See UserPreferences#importPathEnding
26
- const enum Ending { Minimal , Index , JsExtension , TsExtension }
27
25
28
26
// Processed preferences
29
27
interface Preferences {
30
28
readonly relativePreference : RelativePreference ;
31
29
/**
32
30
* @param syntaxImpliedNodeFormat Used when the import syntax implies ESM or CJS irrespective of the mode of the file.
33
31
*/
34
- getAllowedEndingsInPrefererredOrder ( syntaxImpliedNodeFormat ?: SourceFile [ "impliedNodeFormat" ] ) : Ending [ ] ;
32
+ getAllowedEndingsInPrefererredOrder ( syntaxImpliedNodeFormat ?: SourceFile [ "impliedNodeFormat" ] ) : ModuleSpecifierEnding [ ] ;
35
33
}
36
34
37
35
function getPreferences (
38
- host : ModuleSpecifierResolutionHost ,
39
36
{ importModuleSpecifierPreference, importModuleSpecifierEnding } : UserPreferences ,
40
37
compilerOptions : CompilerOptions ,
41
38
importingSourceFile : SourceFile ,
@@ -52,66 +49,38 @@ function getPreferences(
52
49
importModuleSpecifierPreference === "project-relative" ? RelativePreference . ExternalNonRelative :
53
50
RelativePreference . Shortest ,
54
51
getAllowedEndingsInPrefererredOrder : syntaxImpliedNodeFormat => {
55
- if ( syntaxImpliedNodeFormat === ModuleKind . ESNext || isFormatRequiringExtensions ( ) ) {
52
+ if ( syntaxImpliedNodeFormat === ModuleKind . ESNext || ( syntaxImpliedNodeFormat ?? importingSourceFile . impliedNodeFormat ) === ModuleKind . ESNext ) {
56
53
if ( shouldAllowImportingTsExtension ( compilerOptions , importingSourceFile . fileName ) ) {
57
- return [ Ending . TsExtension , Ending . JsExtension ] ;
54
+ return [ ModuleSpecifierEnding . TsExtension , ModuleSpecifierEnding . JsExtension ] ;
58
55
}
59
- return [ Ending . JsExtension ] ;
56
+ return [ ModuleSpecifierEnding . JsExtension ] ;
60
57
}
61
58
if ( getEmitModuleResolutionKind ( compilerOptions ) === ModuleResolutionKind . Classic ) {
62
- return [ Ending . Index , Ending . JsExtension ] ;
59
+ return [ ModuleSpecifierEnding . Index , ModuleSpecifierEnding . JsExtension ] ;
63
60
}
64
61
switch ( preferredEnding ) {
65
- case Ending . JsExtension : return [ Ending . JsExtension , Ending . Minimal , Ending . Index ] ;
66
- case Ending . TsExtension : return [ Ending . TsExtension , Ending . TsExtension , Ending . Minimal , Ending . Index ] ;
67
- case Ending . Index : return [ Ending . Index , Ending . Minimal , Ending . JsExtension ] ;
68
- case Ending . Minimal : return [ Ending . Minimal , Ending . Index , Ending . JsExtension ] ;
62
+ case ModuleSpecifierEnding . JsExtension : return [ ModuleSpecifierEnding . JsExtension , ModuleSpecifierEnding . Minimal , ModuleSpecifierEnding . Index ] ;
63
+ case ModuleSpecifierEnding . TsExtension : return [ ModuleSpecifierEnding . TsExtension , ModuleSpecifierEnding . TsExtension , ModuleSpecifierEnding . Minimal , ModuleSpecifierEnding . Index ] ;
64
+ case ModuleSpecifierEnding . Index : return [ ModuleSpecifierEnding . Index , ModuleSpecifierEnding . Minimal , ModuleSpecifierEnding . JsExtension ] ;
65
+ case ModuleSpecifierEnding . Minimal : return [ ModuleSpecifierEnding . Minimal , ModuleSpecifierEnding . Index , ModuleSpecifierEnding . JsExtension ] ;
69
66
default : Debug . assertNever ( preferredEnding ) ;
70
67
}
71
68
} ,
72
69
} ;
73
70
74
- function getPreferredEnding ( ) : Ending {
71
+ function getPreferredEnding ( ) : ModuleSpecifierEnding {
75
72
if ( oldImportSpecifier !== undefined ) {
76
- if ( hasJSFileExtension ( oldImportSpecifier ) ) return Ending . JsExtension ;
77
- if ( endsWith ( oldImportSpecifier , "/index" ) ) return Ending . Index ;
78
- }
79
- switch ( importModuleSpecifierEnding ) {
80
- case "minimal" : return Ending . Minimal ;
81
- case "index" : return Ending . Index ;
82
- case "js" : return shouldAllowImportingTsExtension ( compilerOptions ) ? Ending . TsExtension : Ending . JsExtension ;
83
- default : return usesExtensionsOnImports ( importingSourceFile )
84
- ? shouldAllowImportingTsExtension ( compilerOptions ) ? Ending . TsExtension : Ending . JsExtension
85
- : Ending . Minimal ;
86
- }
87
- }
88
-
89
- function isFormatRequiringExtensions ( ) {
90
- switch ( getEmitModuleResolutionKind ( compilerOptions ) ) {
91
- case ModuleResolutionKind . Node16 :
92
- case ModuleResolutionKind . NodeNext :
93
- return getImpliedNodeFormatForFile (
94
- importingSourceFile . path ,
95
- host . getPackageJsonInfoCache ?.( ) ,
96
- getModuleResolutionHost ( host ) , compilerOptions ,
97
- ) !== ModuleKind . CommonJS ;
98
- default :
99
- return false ;
73
+ if ( hasJSFileExtension ( oldImportSpecifier ) ) return ModuleSpecifierEnding . JsExtension ;
74
+ if ( endsWith ( oldImportSpecifier , "/index" ) ) return ModuleSpecifierEnding . Index ;
100
75
}
76
+ return getModuleSpecifierEndingPreference (
77
+ importModuleSpecifierEnding ,
78
+ importingSourceFile . impliedNodeFormat ,
79
+ compilerOptions ,
80
+ importingSourceFile ) ;
101
81
}
102
82
}
103
83
104
- function getModuleResolutionHost ( host : ModuleSpecifierResolutionHost ) : ModuleResolutionHost {
105
- return {
106
- fileExists : host . fileExists ,
107
- readFile : Debug . checkDefined ( host . readFile ) ,
108
- directoryExists : host . directoryExists ,
109
- getCurrentDirectory : host . getCurrentDirectory ,
110
- realpath : host . realpath ,
111
- useCaseSensitiveFileNames : host . useCaseSensitiveFileNames ?.( ) ,
112
- } ;
113
- }
114
-
115
84
// `importingSourceFile` and `importingSourceFileName`? Why not just use `importingSourceFile.path`?
116
85
// Because when this is called by the file renamer, `importingSourceFile` is the file being renamed,
117
86
// while `importingSourceFileName` its *new* name. We need a source file just to get its
@@ -126,7 +95,7 @@ export function updateModuleSpecifier(
126
95
oldImportSpecifier : string ,
127
96
options : ModuleSpecifierOptions = { } ,
128
97
) : string | undefined {
129
- const res = getModuleSpecifierWorker ( compilerOptions , importingSourceFile , importingSourceFileName , toFileName , host , getPreferences ( host , { } , compilerOptions , importingSourceFile , oldImportSpecifier ) , { } , options ) ;
98
+ const res = getModuleSpecifierWorker ( compilerOptions , importingSourceFile , importingSourceFileName , toFileName , host , getPreferences ( { } , compilerOptions , importingSourceFile , oldImportSpecifier ) , { } , options ) ;
130
99
if ( res === oldImportSpecifier ) return undefined ;
131
100
return res ;
132
101
}
@@ -146,7 +115,7 @@ export function getModuleSpecifier(
146
115
host : ModuleSpecifierResolutionHost ,
147
116
options : ModuleSpecifierOptions = { } ,
148
117
) : string {
149
- return getModuleSpecifierWorker ( compilerOptions , importingSourceFile , importingSourceFileName , toFileName , host , getPreferences ( host , { } , compilerOptions , importingSourceFile ) , { } , options ) ;
118
+ return getModuleSpecifierWorker ( compilerOptions , importingSourceFile , importingSourceFileName , toFileName , host , getPreferences ( { } , compilerOptions , importingSourceFile ) , { } , options ) ;
150
119
}
151
120
152
121
/** @internal */
@@ -279,7 +248,7 @@ function computeModuleSpecifiers(
279
248
options : ModuleSpecifierOptions = { } ,
280
249
) : readonly string [ ] {
281
250
const info = getInfo ( importingSourceFile . path , host ) ;
282
- const preferences = getPreferences ( host , userPreferences , compilerOptions , importingSourceFile ) ;
251
+ const preferences = getPreferences ( userPreferences , compilerOptions , importingSourceFile ) ;
283
252
const existingSpecifier = forEach ( modulePaths , modulePath => forEach (
284
253
host . getFileIncludeReasons ( ) . get ( toPath ( modulePath . path , host . getCurrentDirectory ( ) , info . getCanonicalFileName ) ) ,
285
254
reason => {
@@ -458,10 +427,6 @@ export function countPathComponents(path: string): number {
458
427
return count ;
459
428
}
460
429
461
- function usesExtensionsOnImports ( { imports } : SourceFile ) : boolean {
462
- return firstDefined ( imports , ( { text } ) => pathIsRelative ( text ) ? ( hasJSFileExtension ( text ) || hasTSFileExtension ( text ) ) : undefined ) || false ;
463
- }
464
-
465
430
function comparePathsByRedirectAndNumberOfDirectorySeparators ( a : ModulePath , b : ModulePath ) {
466
431
return compareBooleans ( b . isRedirect , a . isRedirect ) || compareNumberOfDirectorySeparators ( a . path , b . path ) ;
467
432
}
@@ -648,7 +613,7 @@ function tryGetModuleNameFromAmbientModule(moduleSymbol: Symbol, checker: TypeCh
648
613
}
649
614
}
650
615
651
- function tryGetModuleNameFromPaths ( relativeToBaseUrl : string , paths : MapLike < readonly string [ ] > , allowedEndings : Ending [ ] , host : ModuleSpecifierResolutionHost , compilerOptions : CompilerOptions ) : string | undefined {
616
+ function tryGetModuleNameFromPaths ( relativeToBaseUrl : string , paths : MapLike < readonly string [ ] > , allowedEndings : ModuleSpecifierEnding [ ] , host : ModuleSpecifierResolutionHost , compilerOptions : CompilerOptions ) : string | undefined {
652
617
for ( const key in paths ) {
653
618
for ( const patternText of paths [ key ] ) {
654
619
const pattern = normalizePath ( patternText ) ;
@@ -689,7 +654,7 @@ function tryGetModuleNameFromPaths(relativeToBaseUrl: string, paths: MapLike<rea
689
654
// resolution, but as long criteria (b) above is met, I don't think its result needs to be the highest priority result
690
655
// in module specifier generation. I have included it last, as it's difficult to tell exactly where it should be
691
656
// sorted among the others for a particular value of `importModuleSpecifierEnding`.
692
- const candidates : { ending : Ending | undefined , value : string } [ ] = allowedEndings . map ( ending => ( {
657
+ const candidates : { ending : ModuleSpecifierEnding | undefined , value : string } [ ] = allowedEndings . map ( ending => ( {
693
658
ending,
694
659
value : processEnding ( relativeToBaseUrl , ending , compilerOptions )
695
660
} ) ) ;
@@ -712,23 +677,23 @@ function tryGetModuleNameFromPaths(relativeToBaseUrl: string, paths: MapLike<rea
712
677
}
713
678
}
714
679
else if (
715
- some ( candidates , c => c . ending !== Ending . Minimal && pattern === c . value ) ||
716
- some ( candidates , c => c . ending === Ending . Minimal && pattern === c . value && validateEnding ( c ) )
680
+ some ( candidates , c => c . ending !== ModuleSpecifierEnding . Minimal && pattern === c . value ) ||
681
+ some ( candidates , c => c . ending === ModuleSpecifierEnding . Minimal && pattern === c . value && validateEnding ( c ) )
717
682
) {
718
683
return key ;
719
684
}
720
685
}
721
686
}
722
687
723
- function validateEnding ( { ending, value } : { ending : Ending | undefined , value : string } ) {
688
+ function validateEnding ( { ending, value } : { ending : ModuleSpecifierEnding | undefined , value : string } ) {
724
689
// Optimization: `removeExtensionAndIndexPostFix` can query the file system (a good bit) if `ending` is `Minimal`, the basename
725
690
// is 'index', and a `host` is provided. To avoid that until it's unavoidable, we ran the function with no `host` above. Only
726
691
// here, after we've checked that the minimal ending is indeed a match (via the length and prefix/suffix checks / `some` calls),
727
692
// do we check that the host-validated result is consistent with the answer we got before. If it's not, it falls back to the
728
- // `Ending .Index` result, which should already be in the list of candidates if `Minimal` was. (Note: the assumption here is
693
+ // `ModuleSpecifierEnding .Index` result, which should already be in the list of candidates if `Minimal` was. (Note: the assumption here is
729
694
// that every module resolution mode that supports dropping extensions also supports dropping `/index`. Like literally
730
695
// everything else in this file, this logic needs to be updated if that's not true in some future module resolution mode.)
731
- return ending !== Ending . Minimal || value === processEnding ( relativeToBaseUrl , ending , compilerOptions , host ) ;
696
+ return ending !== ModuleSpecifierEnding . Minimal || value === processEnding ( relativeToBaseUrl , ending , compilerOptions , host ) ;
732
697
}
733
698
}
734
699
@@ -803,7 +768,7 @@ function tryGetModuleNameFromExports(options: CompilerOptions, targetFilePath: s
803
768
return undefined ;
804
769
}
805
770
806
- function tryGetModuleNameFromRootDirs ( rootDirs : readonly string [ ] , moduleFileName : string , sourceDirectory : string , getCanonicalFileName : ( file : string ) => string , ending : Ending , compilerOptions : CompilerOptions ) : string | undefined {
771
+ function tryGetModuleNameFromRootDirs ( rootDirs : readonly string [ ] , moduleFileName : string , sourceDirectory : string , getCanonicalFileName : ( file : string ) => string , ending : ModuleSpecifierEnding , compilerOptions : CompilerOptions ) : string | undefined {
807
772
const normalizedTargetPaths = getPathsRelativeToRootDirs ( moduleFileName , rootDirs , getCanonicalFileName ) ;
808
773
if ( normalizedTargetPaths === undefined ) {
809
774
return undefined ;
@@ -831,7 +796,7 @@ function tryGetModuleNameAsNodeModule({ path, isRedirect }: ModulePath, { getCan
831
796
832
797
// Simplify the full file path to something that can be resolved by Node.
833
798
834
- const preferences = getPreferences ( host , userPreferences , options , importingSourceFile ) ;
799
+ const preferences = getPreferences ( userPreferences , options , importingSourceFile ) ;
835
800
const allowedEndings = preferences . getAllowedEndingsInPrefererredOrder ( ) ;
836
801
let moduleSpecifier = path ;
837
802
let isPackageRootPath = false ;
@@ -972,25 +937,25 @@ function getPathsRelativeToRootDirs(path: string, rootDirs: readonly string[], g
972
937
} ) ;
973
938
}
974
939
975
- function processEnding ( fileName : string , ending : Ending , options : CompilerOptions , host ?: ModuleSpecifierResolutionHost ) : string {
940
+ function processEnding ( fileName : string , ending : ModuleSpecifierEnding , options : CompilerOptions , host ?: ModuleSpecifierResolutionHost ) : string {
976
941
if ( fileExtensionIsOneOf ( fileName , [ Extension . Json , Extension . Mjs , Extension . Cjs ] ) ) return fileName ;
977
942
const noExtension = removeFileExtension ( fileName ) ;
978
943
if ( fileName === noExtension ) return fileName ;
979
944
if ( fileExtensionIsOneOf ( fileName , [ Extension . Dmts , Extension . Mts , Extension . Dcts , Extension . Cts ] ) ) return noExtension + getJSExtensionForFile ( fileName , options ) ;
980
945
switch ( ending ) {
981
- case Ending . Minimal :
946
+ case ModuleSpecifierEnding . Minimal :
982
947
const withoutIndex = removeSuffix ( noExtension , "/index" ) ;
983
948
if ( host && withoutIndex !== noExtension && tryGetAnyFileFromPath ( host , withoutIndex ) ) {
984
949
// Can't remove index if there's a file by the same name as the directory.
985
950
// Probably more callers should pass `host` so we can determine this?
986
951
return noExtension ;
987
952
}
988
953
return withoutIndex ;
989
- case Ending . Index :
954
+ case ModuleSpecifierEnding . Index :
990
955
return noExtension ;
991
- case Ending . JsExtension :
956
+ case ModuleSpecifierEnding . JsExtension :
992
957
return noExtension + getJSExtensionForFile ( fileName , options ) ;
993
- case Ending . TsExtension :
958
+ case ModuleSpecifierEnding . TsExtension :
994
959
return fileName ;
995
960
default :
996
961
return Debug . assertNever ( ending ) ;
0 commit comments