3
3
namespace ts . moduleSpecifiers {
4
4
export interface ModuleSpecifierPreferences {
5
5
readonly importModuleSpecifierPreference ?: "relative" | "non-relative" ;
6
+ readonly includeExtensionInImports ?: boolean
6
7
}
7
8
8
9
// Note: importingSourceFile is just for usesJsExtensionOnImports
@@ -17,7 +18,7 @@ namespace ts.moduleSpecifiers {
17
18
) : string {
18
19
const info = getInfo ( compilerOptions , importingSourceFile , importingSourceFileName , host ) ;
19
20
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 ) ) ||
21
22
first ( getLocalModuleSpecifiers ( toFileName , info , compilerOptions , preferences ) ) ;
22
23
}
23
24
@@ -39,46 +40,47 @@ namespace ts.moduleSpecifiers {
39
40
}
40
41
const modulePaths = getAllModulePaths ( files , getSourceFileOfNode ( moduleSymbol . valueDeclaration ) . fileName , info . getCanonicalFileName , host ) ;
41
42
42
- const global = mapDefined ( modulePaths , moduleFileName => getGlobalModuleSpecifier ( moduleFileName , info , host , compilerOptions ) ) ;
43
+ const global = mapDefined ( modulePaths , moduleFileName => getGlobalModuleSpecifier ( moduleFileName , info , host , compilerOptions , preferences ) ) ;
43
44
return global . length ? global . map ( g => [ g ] ) : modulePaths . map ( moduleFileName =>
44
45
getLocalModuleSpecifiers ( moduleFileName , info , compilerOptions , preferences ) ) ;
45
46
}
46
47
47
48
interface Info {
48
49
readonly moduleResolutionKind : ModuleResolutionKind ;
49
- readonly addJsExtension : boolean ;
50
+ readonly addExtension : boolean ;
50
51
readonly getCanonicalFileName : GetCanonicalFileName ;
51
52
readonly sourceDirectory : Path ;
52
53
}
53
54
// importingSourceFileName is separate because getEditsForFileRename may need to specify an updated path
54
55
function getInfo ( compilerOptions : CompilerOptions , importingSourceFile : SourceFile , importingSourceFileName : Path , host : ModuleSpecifierResolutionHost ) : Info {
55
56
const moduleResolutionKind = getEmitModuleResolutionKind ( compilerOptions ) ;
56
- const addJsExtension = usesJsExtensionOnImports ( importingSourceFile ) ;
57
+ const addExtension = usesExtensionOnImports ( importingSourceFile ) ;
57
58
const getCanonicalFileName = createGetCanonicalFileName ( host . useCaseSensitiveFileNames ? host . useCaseSensitiveFileNames ( ) : true ) ;
58
59
const sourceDirectory = getDirectoryPath ( importingSourceFileName ) ;
59
- return { moduleResolutionKind, addJsExtension , getCanonicalFileName, sourceDirectory } ;
60
+ return { moduleResolutionKind, addExtension , getCanonicalFileName, sourceDirectory } ;
60
61
}
61
62
62
63
function getGlobalModuleSpecifier (
63
64
moduleFileName : string ,
64
- { addJsExtension , getCanonicalFileName, sourceDirectory } : Info ,
65
+ { addExtension , getCanonicalFileName, sourceDirectory } : Info ,
65
66
host : ModuleSpecifierResolutionHost ,
66
67
compilerOptions : CompilerOptions ,
68
+ preferences : ModuleSpecifierPreferences
67
69
) {
68
- return tryGetModuleNameFromTypeRoots ( compilerOptions , host , getCanonicalFileName , moduleFileName , addJsExtension )
70
+ return tryGetModuleNameFromTypeRoots ( compilerOptions , host , getCanonicalFileName , moduleFileName , addExtension , preferences )
69
71
|| tryGetModuleNameAsNodeModule ( compilerOptions , moduleFileName , host , getCanonicalFileName , sourceDirectory ) ;
70
72
}
71
73
72
74
function getLocalModuleSpecifiers (
73
75
moduleFileName : string ,
74
- { moduleResolutionKind, addJsExtension , getCanonicalFileName, sourceDirectory } : Info ,
76
+ { moduleResolutionKind, addExtension , getCanonicalFileName, sourceDirectory } : Info ,
75
77
compilerOptions : CompilerOptions ,
76
78
preferences : ModuleSpecifierPreferences ,
77
79
) : ReadonlyArray < string > {
78
80
const { baseUrl, paths, rootDirs } = compilerOptions ;
79
81
80
82
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 ) ;
82
84
if ( ! baseUrl || preferences . importModuleSpecifierPreference === "relative" ) {
83
85
return [ relativePath ] ;
84
86
}
@@ -88,7 +90,7 @@ namespace ts.moduleSpecifiers {
88
90
return [ relativePath ] ;
89
91
}
90
92
91
- const importRelativeToBaseUrl = removeExtensionAndIndexPostFix ( relativeToBaseUrl , moduleResolutionKind , addJsExtension ) ;
93
+ const importRelativeToBaseUrl = removeExtensionAndIndexPostFix ( relativeToBaseUrl , moduleResolutionKind , addExtension , compilerOptions , preferences ) ;
92
94
if ( paths ) {
93
95
const fromPaths = tryGetModuleNameFromPaths ( removeFileExtension ( relativeToBaseUrl ) , importRelativeToBaseUrl , paths ) ;
94
96
if ( fromPaths ) {
@@ -138,8 +140,8 @@ namespace ts.moduleSpecifiers {
138
140
return relativeFirst ? [ relativePath , importRelativeToBaseUrl ] : [ importRelativeToBaseUrl , relativePath ] ;
139
141
}
140
142
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 ;
143
145
}
144
146
145
147
function discoverProbableSymlinks ( files : ReadonlyArray < SourceFile > , getCanonicalFileName : ( file : string ) => string , host : ModuleSpecifierResolutionHost ) {
@@ -256,14 +258,15 @@ namespace ts.moduleSpecifiers {
256
258
host : GetEffectiveTypeRootsHost ,
257
259
getCanonicalFileName : ( file : string ) => string ,
258
260
moduleFileName : string ,
259
- addJsExtension : boolean ,
261
+ addExtension : boolean ,
262
+ preferences : ModuleSpecifierPreferences
260
263
) : string | undefined {
261
264
const roots = getEffectiveTypeRoots ( options , host ) ;
262
265
return firstDefined ( roots , unNormalizedTypeRoot => {
263
266
const typeRoot = toPath ( unNormalizedTypeRoot , /*basePath*/ undefined , getCanonicalFileName ) ;
264
267
if ( startsWith ( moduleFileName , typeRoot ) ) {
265
268
// 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 ) ;
267
270
}
268
271
} ) ;
269
272
}
@@ -408,10 +411,25 @@ namespace ts.moduleSpecifiers {
408
411
} ) ;
409
412
}
410
413
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 {
412
429
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
415
433
: moduleResolutionKind === ModuleResolutionKind . NodeJs
416
434
? removeSuffix ( noExtension , "/index" )
417
435
: noExtension ;
0 commit comments