33namespace ts . moduleSpecifiers {
44 export interface ModuleSpecifierPreferences {
55 readonly importModuleSpecifierPreference ?: "relative" | "non-relative" ;
6+ readonly includeExtensionInImports ?: boolean
67 }
78
89 // Note: importingSourceFile is just for usesJsExtensionOnImports
@@ -17,7 +18,7 @@ namespace ts.moduleSpecifiers {
1718 ) : string {
1819 const info = getInfo ( compilerOptions , importingSourceFile , importingSourceFileName , host ) ;
1920 const modulePaths = getAllModulePaths ( files , toFileName , info . getCanonicalFileName , host ) ;
20- return firstDefined ( modulePaths , moduleFileName => getGlobalModuleSpecifier ( moduleFileName , info , host , compilerOptions ) ) ||
21+ return firstDefined ( modulePaths , moduleFileName => getGlobalModuleSpecifier ( moduleFileName , info , host , compilerOptions , preferences ) ) ||
2122 first ( getLocalModuleSpecifiers ( toFileName , info , compilerOptions , preferences ) ) ;
2223 }
2324
@@ -39,46 +40,47 @@ namespace ts.moduleSpecifiers {
3940 }
4041 const modulePaths = getAllModulePaths ( files , getSourceFileOfNode ( moduleSymbol . valueDeclaration ) . fileName , info . getCanonicalFileName , host ) ;
4142
42- const global = mapDefined ( modulePaths , moduleFileName => getGlobalModuleSpecifier ( moduleFileName , info , host , compilerOptions ) ) ;
43+ const global = mapDefined ( modulePaths , moduleFileName => getGlobalModuleSpecifier ( moduleFileName , info , host , compilerOptions , preferences ) ) ;
4344 return global . length ? global . map ( g => [ g ] ) : modulePaths . map ( moduleFileName =>
4445 getLocalModuleSpecifiers ( moduleFileName , info , compilerOptions , preferences ) ) ;
4546 }
4647
4748 interface Info {
4849 readonly moduleResolutionKind : ModuleResolutionKind ;
49- readonly addJsExtension : boolean ;
50+ readonly addExtension : boolean ;
5051 readonly getCanonicalFileName : GetCanonicalFileName ;
5152 readonly sourceDirectory : Path ;
5253 }
5354 // importingSourceFileName is separate because getEditsForFileRename may need to specify an updated path
5455 function getInfo ( compilerOptions : CompilerOptions , importingSourceFile : SourceFile , importingSourceFileName : Path , host : ModuleSpecifierResolutionHost ) : Info {
5556 const moduleResolutionKind = getEmitModuleResolutionKind ( compilerOptions ) ;
56- const addJsExtension = usesJsExtensionOnImports ( importingSourceFile ) ;
57+ const addExtension = usesExtensionOnImports ( importingSourceFile ) ;
5758 const getCanonicalFileName = createGetCanonicalFileName ( host . useCaseSensitiveFileNames ? host . useCaseSensitiveFileNames ( ) : true ) ;
5859 const sourceDirectory = getDirectoryPath ( importingSourceFileName ) ;
59- return { moduleResolutionKind, addJsExtension , getCanonicalFileName, sourceDirectory } ;
60+ return { moduleResolutionKind, addExtension , getCanonicalFileName, sourceDirectory } ;
6061 }
6162
6263 function getGlobalModuleSpecifier (
6364 moduleFileName : string ,
64- { addJsExtension , getCanonicalFileName, sourceDirectory } : Info ,
65+ { addExtension , getCanonicalFileName, sourceDirectory } : Info ,
6566 host : ModuleSpecifierResolutionHost ,
6667 compilerOptions : CompilerOptions ,
68+ preferences : ModuleSpecifierPreferences
6769 ) {
68- return tryGetModuleNameFromTypeRoots ( compilerOptions , host , getCanonicalFileName , moduleFileName , addJsExtension )
70+ return tryGetModuleNameFromTypeRoots ( compilerOptions , host , getCanonicalFileName , moduleFileName , addExtension , preferences )
6971 || tryGetModuleNameAsNodeModule ( compilerOptions , moduleFileName , host , getCanonicalFileName , sourceDirectory ) ;
7072 }
7173
7274 function getLocalModuleSpecifiers (
7375 moduleFileName : string ,
74- { moduleResolutionKind, addJsExtension , getCanonicalFileName, sourceDirectory } : Info ,
76+ { moduleResolutionKind, addExtension , getCanonicalFileName, sourceDirectory } : Info ,
7577 compilerOptions : CompilerOptions ,
7678 preferences : ModuleSpecifierPreferences ,
7779 ) : ReadonlyArray < string > {
7880 const { baseUrl, paths, rootDirs } = compilerOptions ;
7981
8082 const relativePath = rootDirs && tryGetModuleNameFromRootDirs ( rootDirs , moduleFileName , sourceDirectory , getCanonicalFileName ) ||
81- removeExtensionAndIndexPostFix ( ensurePathIsNonModuleName ( getRelativePathFromDirectory ( sourceDirectory , moduleFileName , getCanonicalFileName ) ) , moduleResolutionKind , addJsExtension ) ;
83+ removeExtensionAndIndexPostFix ( ensurePathIsNonModuleName ( getRelativePathFromDirectory ( sourceDirectory , moduleFileName , getCanonicalFileName ) ) , moduleResolutionKind , addExtension , compilerOptions , preferences ) ;
8284 if ( ! baseUrl || preferences . importModuleSpecifierPreference === "relative" ) {
8385 return [ relativePath ] ;
8486 }
@@ -88,7 +90,7 @@ namespace ts.moduleSpecifiers {
8890 return [ relativePath ] ;
8991 }
9092
91- const importRelativeToBaseUrl = removeExtensionAndIndexPostFix ( relativeToBaseUrl , moduleResolutionKind , addJsExtension ) ;
93+ const importRelativeToBaseUrl = removeExtensionAndIndexPostFix ( relativeToBaseUrl , moduleResolutionKind , addExtension , compilerOptions , preferences ) ;
9294 if ( paths ) {
9395 const fromPaths = tryGetModuleNameFromPaths ( removeFileExtension ( relativeToBaseUrl ) , importRelativeToBaseUrl , paths ) ;
9496 if ( fromPaths ) {
@@ -138,8 +140,8 @@ namespace ts.moduleSpecifiers {
138140 return relativeFirst ? [ relativePath , importRelativeToBaseUrl ] : [ importRelativeToBaseUrl , relativePath ] ;
139141 }
140142
141- function usesJsExtensionOnImports ( { imports } : SourceFile ) : boolean {
142- return firstDefined ( imports , ( { text } ) => pathIsRelative ( text ) ? fileExtensionIs ( text , Extension . Js ) : undefined ) || false ;
143+ function usesExtensionOnImports ( { imports } : SourceFile ) : boolean {
144+ return firstDefined ( imports , ( { text } ) => pathIsRelative ( text ) ? fileExtensionIsOneOf ( text , [ Extension . Js , Extension . Jsx ] ) : undefined ) || false ;
143145 }
144146
145147 function discoverProbableSymlinks ( files : ReadonlyArray < SourceFile > , getCanonicalFileName : ( file : string ) => string , host : ModuleSpecifierResolutionHost ) {
@@ -256,14 +258,15 @@ namespace ts.moduleSpecifiers {
256258 host : GetEffectiveTypeRootsHost ,
257259 getCanonicalFileName : ( file : string ) => string ,
258260 moduleFileName : string ,
259- addJsExtension : boolean ,
261+ addExtension : boolean ,
262+ preferences : ModuleSpecifierPreferences
260263 ) : string | undefined {
261264 const roots = getEffectiveTypeRoots ( options , host ) ;
262265 return firstDefined ( roots , unNormalizedTypeRoot => {
263266 const typeRoot = toPath ( unNormalizedTypeRoot , /*basePath*/ undefined , getCanonicalFileName ) ;
264267 if ( startsWith ( moduleFileName , typeRoot ) ) {
265268 // For a type definition, we can strip `/index` even with classic resolution.
266- return removeExtensionAndIndexPostFix ( moduleFileName . substring ( typeRoot . length + 1 ) , ModuleResolutionKind . NodeJs , addJsExtension ) ;
269+ return removeExtensionAndIndexPostFix ( moduleFileName . substring ( typeRoot . length + 1 ) , ModuleResolutionKind . NodeJs , addExtension , options , preferences ) ;
267270 }
268271 } ) ;
269272 }
@@ -408,10 +411,25 @@ namespace ts.moduleSpecifiers {
408411 } ) ;
409412 }
410413
411- function removeExtensionAndIndexPostFix ( fileName : string , moduleResolutionKind : ModuleResolutionKind , addJsExtension : boolean ) : string {
414+ function tryGetActualExtension ( text : string , compilerOptions : CompilerOptions ) {
415+ const extension = pathIsRelative ( text ) && tryGetExtensionFromPath ( text ) ;
416+ if ( ! extension ) return undefined ;
417+
418+ switch ( extension ) {
419+ case Extension . Ts :
420+ return Extension . Js ;
421+ case Extension . Tsx :
422+ return compilerOptions . jsx === JsxEmit . React || compilerOptions . jsx === JsxEmit . ReactNative ? Extension . Js : Extension . Jsx ;
423+ default :
424+ return extension ;
425+ }
426+ }
427+
428+ function removeExtensionAndIndexPostFix ( fileName : string , moduleResolutionKind : ModuleResolutionKind , addJsExtension : boolean , compilerOptions : CompilerOptions , preferences : ModuleSpecifierPreferences ) : string {
412429 const noExtension = removeFileExtension ( fileName ) ;
413- return addJsExtension
414- ? noExtension + ".js"
430+ const actualExtension = tryGetActualExtension ( fileName , compilerOptions ) ;
431+ return ( actualExtension && ( preferences . includeExtensionInImports !== undefined && preferences . includeExtensionInImports || addJsExtension ) )
432+ ? noExtension + actualExtension
415433 : moduleResolutionKind === ModuleResolutionKind . NodeJs
416434 ? removeSuffix ( noExtension , "/index" )
417435 : noExtension ;
0 commit comments