33namespace ts . moduleSpecifiers {
44 export interface ModuleSpecifierPreferences {
55 importModuleSpecifierPreference ?: "relative" | "non-relative" ;
6- includeExtensionInImports ?: Extension . Js | Extension . Jsx ;
6+ includeExtensionInImports ?: boolean ;
77 }
88
99 // Note: fromSourceFile is just for usesJsExtensionOnImports
10- export function getModuleSpecifier ( compilerOptions : CompilerOptions , fromSourceFile : SourceFile , fromSourceFileName : string , toFileName : string , host : ModuleSpecifierResolutionHost , preferences : ModuleSpecifierPreferences = { } ) {
11- const info = getInfo ( compilerOptions , fromSourceFile , fromSourceFileName , host , preferences ) ;
12- return getGlobalModuleSpecifier ( toFileName , info , host , compilerOptions ) ||
10+ export function getModuleSpecifier ( compilerOptions : CompilerOptions , fromSourceFileName : string , toFileName : string , host : ModuleSpecifierResolutionHost , preferences : ModuleSpecifierPreferences = { } ) {
11+ const info = getInfo ( compilerOptions , fromSourceFileName , host ) ;
12+ return getGlobalModuleSpecifier ( toFileName , info , host , compilerOptions , preferences ) ||
1313 first ( getLocalModuleSpecifiers ( toFileName , info , compilerOptions , preferences ) ) ;
1414 }
1515
1616 // For each symlink/original for a module, returns a list of ways to import that file.
1717 export function getModuleSpecifiers (
1818 moduleSymbol : Symbol ,
1919 compilerOptions : CompilerOptions ,
20- importingSourceFile : SourceFile ,
20+ importingSourceFileName : string ,
2121 host : ModuleSpecifierResolutionHost ,
2222 files : ReadonlyArray < SourceFile > ,
2323 preferences : ModuleSpecifierPreferences ,
2424 ) : ReadonlyArray < ReadonlyArray < string > > {
2525 const ambient = tryGetModuleNameFromAmbientModule ( moduleSymbol ) ;
2626 if ( ambient ) return [ [ ambient ] ] ;
2727
28- const info = getInfo ( compilerOptions , importingSourceFile , importingSourceFile . path , host , preferences ) ;
28+ const info = getInfo ( compilerOptions , importingSourceFileName , host ) ;
2929 if ( ! files ) {
3030 return Debug . fail ( "Files list must be present to resolve symlinks in specifier resolution" ) ;
3131 }
3232 const modulePaths = getAllModulePaths ( files , getSourceFileOfNode ( moduleSymbol . valueDeclaration ) , info . getCanonicalFileName , host ) ;
3333
34- const global = mapDefined ( modulePaths , moduleFileName => getGlobalModuleSpecifier ( moduleFileName , info , host , compilerOptions ) ) ;
34+ const global = mapDefined ( modulePaths , moduleFileName => getGlobalModuleSpecifier ( moduleFileName , info , host , compilerOptions , preferences ) ) ;
3535 return global . length ? global . map ( g => [ g ] ) : modulePaths . map ( moduleFileName =>
3636 getLocalModuleSpecifiers ( moduleFileName , info , compilerOptions , preferences ) ) ;
3737 }
3838
3939 interface Info {
4040 readonly moduleResolutionKind : ModuleResolutionKind ;
41- readonly addExtension : Extension . Js | Extension . Jsx | undefined ;
4241 readonly getCanonicalFileName : GetCanonicalFileName ;
4342 readonly sourceDirectory : string ;
4443 }
45- // importingSourceFileName is separate because getEditsForFileRename may need to specify an updated path
46- function getInfo ( compilerOptions : CompilerOptions , importingSourceFile : SourceFile , importingSourceFileName : string , host : ModuleSpecifierResolutionHost , preferences : ModuleSpecifierPreferences ) : Info {
44+
45+ function getInfo ( compilerOptions : CompilerOptions , importingSourceFileName : string , host : ModuleSpecifierResolutionHost ) : Info {
4746 const moduleResolutionKind = getEmitModuleResolutionKind ( compilerOptions ) ;
48- const addExtension = usesExtensionOnImports ( importingSourceFile , preferences ) ;
4947 const getCanonicalFileName = createGetCanonicalFileName ( host . useCaseSensitiveFileNames ? host . useCaseSensitiveFileNames ( ) : true ) ;
5048 const sourceDirectory = getDirectoryPath ( importingSourceFileName ) ;
51- return { moduleResolutionKind, addExtension , getCanonicalFileName, sourceDirectory } ;
49+ return { moduleResolutionKind, getCanonicalFileName, sourceDirectory } ;
5250 }
5351
5452 function getGlobalModuleSpecifier (
5553 moduleFileName : string ,
56- { addExtension , getCanonicalFileName, sourceDirectory } : Info ,
54+ { getCanonicalFileName, sourceDirectory } : Info ,
5755 host : ModuleSpecifierResolutionHost ,
5856 compilerOptions : CompilerOptions ,
57+ preferences : ModuleSpecifierPreferences ,
5958 ) {
60- return tryGetModuleNameFromTypeRoots ( compilerOptions , host , getCanonicalFileName , moduleFileName , addExtension )
59+ return tryGetModuleNameFromTypeRoots ( compilerOptions , host , getCanonicalFileName , moduleFileName , preferences )
6160 || tryGetModuleNameAsNodeModule ( compilerOptions , moduleFileName , host , getCanonicalFileName , sourceDirectory )
6261 || compilerOptions . rootDirs && tryGetModuleNameFromRootDirs ( compilerOptions . rootDirs , moduleFileName , sourceDirectory , getCanonicalFileName ) ;
6362 }
6463
6564 function getLocalModuleSpecifiers (
6665 moduleFileName : string ,
67- { moduleResolutionKind, addExtension , getCanonicalFileName, sourceDirectory } : Info ,
66+ { moduleResolutionKind, getCanonicalFileName, sourceDirectory } : Info ,
6867 compilerOptions : CompilerOptions ,
6968 preferences : ModuleSpecifierPreferences ,
7069 ) {
7170 const { baseUrl, paths } = compilerOptions ;
7271
73- const relativePath = removeExtensionAndIndexPostFix ( ensurePathIsNonModuleName ( getRelativePathFromDirectory ( sourceDirectory , moduleFileName , getCanonicalFileName ) ) , moduleResolutionKind , addExtension ) ;
72+ const relativePath = removeExtensionAndIndexPostFix ( ensurePathIsNonModuleName ( getRelativePathFromDirectory ( sourceDirectory , moduleFileName , getCanonicalFileName ) ) , moduleResolutionKind , preferences ) ;
7473 if ( ! baseUrl || preferences . importModuleSpecifierPreference === "relative" ) {
7574 return [ relativePath ] ;
7675 }
@@ -80,7 +79,7 @@ namespace ts.moduleSpecifiers {
8079 return [ relativePath ] ;
8180 }
8281
83- const importRelativeToBaseUrl = removeExtensionAndIndexPostFix ( relativeToBaseUrl , moduleResolutionKind , addExtension ) ;
82+ const importRelativeToBaseUrl = removeExtensionAndIndexPostFix ( relativeToBaseUrl , moduleResolutionKind , preferences ) ;
8483 if ( paths ) {
8584 const fromPaths = tryGetModuleNameFromPaths ( removeFileExtension ( relativeToBaseUrl ) , importRelativeToBaseUrl , paths ) ;
8685 if ( fromPaths ) {
@@ -130,16 +129,6 @@ namespace ts.moduleSpecifiers {
130129 return relativeFirst ? [ relativePath , importRelativeToBaseUrl ] : [ importRelativeToBaseUrl , relativePath ] ;
131130 }
132131
133- function tryGetFileExtension ( text : string ) {
134- return pathIsRelative ( text ) && fileExtensionIsOneOf ( text , [ Extension . Js , Extension . Jsx ] ) ? (
135- fileExtensionIs ( text , Extension . Js ) ? Extension . Js : Extension . Jsx
136- ) : undefined ;
137- }
138-
139- function usesExtensionOnImports ( { imports } : SourceFile , preferences : ModuleSpecifierPreferences ) : Extension . Js | Extension . Jsx | undefined {
140- return preferences . includeExtensionInImports === undefined ? firstDefined ( imports , ( { text } ) => tryGetFileExtension ( text ) ) : preferences . includeExtensionInImports ;
141- }
142-
143132 function discoverProbableSymlinks ( files : ReadonlyArray < SourceFile > , getCanonicalFileName : ( file : string ) => string , host : ModuleSpecifierResolutionHost ) {
144133 const symlinks = mapDefined ( files , sf =>
145134 sf . resolvedModules && firstDefinedIterator ( sf . resolvedModules . values ( ) , res =>
@@ -251,14 +240,14 @@ namespace ts.moduleSpecifiers {
251240 host : GetEffectiveTypeRootsHost ,
252241 getCanonicalFileName : ( file : string ) => string ,
253242 moduleFileName : string ,
254- addExtension : Extension . Js | Extension . Jsx | undefined ,
243+ preferences : ModuleSpecifierPreferences
255244 ) : string | undefined {
256245 const roots = getEffectiveTypeRoots ( options , host ) ;
257246 return firstDefined ( roots , unNormalizedTypeRoot => {
258247 const typeRoot = toPath ( unNormalizedTypeRoot , /*basePath*/ undefined , getCanonicalFileName ) ;
259248 if ( startsWith ( moduleFileName , typeRoot ) ) {
260249 // For a type definition, we can strip `/index` even with classic resolution.
261- return removeExtensionAndIndexPostFix ( moduleFileName . substring ( typeRoot . length + 1 ) , ModuleResolutionKind . NodeJs , addExtension ) ;
250+ return removeExtensionAndIndexPostFix ( moduleFileName . substring ( typeRoot . length + 1 ) , ModuleResolutionKind . NodeJs , preferences ) ;
262251 }
263252 } ) ;
264253 }
@@ -403,10 +392,21 @@ namespace ts.moduleSpecifiers {
403392 } ) ;
404393 }
405394
406- function removeExtensionAndIndexPostFix ( fileName : string , moduleResolutionKind : ModuleResolutionKind , addExtension : Extension . Js | Extension . Jsx | undefined ) : string {
395+ function tryGetActualExtension ( text : string ) {
396+ return pathIsRelative ( text ) && (
397+ fileExtensionIsOneOf ( text , [ Extension . Js , Extension . Ts ] )
398+ ? Extension . Js
399+ : fileExtensionIsOneOf ( text , [ Extension . Jsx , Extension . Tsx ] )
400+ ? Extension . Jsx
401+ : undefined
402+ ) ;
403+ }
404+
405+ function removeExtensionAndIndexPostFix ( fileName : string , moduleResolutionKind : ModuleResolutionKind , preferences : ModuleSpecifierPreferences ) : string {
407406 const noExtension = removeFileExtension ( fileName ) ;
408- return addExtension
409- ? noExtension + addExtension
407+ const actualExtension = tryGetActualExtension ( fileName ) ;
408+ return ( preferences . includeExtensionInImports && actualExtension )
409+ ? noExtension + actualExtension
410410 : moduleResolutionKind === ModuleResolutionKind . NodeJs
411411 ? removeSuffix ( noExtension , "/index" )
412412 : noExtension ;
0 commit comments