1
1
// Used by importFixes to synthesize import module specifiers.
2
2
/* @internal */
3
3
namespace ts . moduleSpecifiers {
4
+ export interface ModuleSpecifierPreferences {
5
+ importModuleSpecifierPreference ?: "relative" | "non-relative" ;
6
+ }
7
+
4
8
// Note: fromSourceFile is just for usesJsExtensionOnImports
5
- export function getModuleSpecifier ( program : Program , fromSourceFile : SourceFile , fromSourceFileName : string , toFileName : string , host : LanguageServiceHost , preferences : UserPreferences ) {
6
- const info = getInfo ( program . getCompilerOptions ( ) , fromSourceFile , fromSourceFileName , host ) ;
7
- const compilerOptions = program . getCompilerOptions ( ) ;
9
+ export function getModuleSpecifier ( compilerOptions : CompilerOptions , fromSourceFile : SourceFile , fromSourceFileName : string , toFileName : string , host : ModuleSpecifierResolutionHost , preferences : ModuleSpecifierPreferences = { } ) {
10
+ const info = getInfo ( compilerOptions , fromSourceFile , fromSourceFileName , host ) ;
8
11
return getGlobalModuleSpecifier ( toFileName , info , host , compilerOptions ) ||
9
12
first ( getLocalModuleSpecifiers ( toFileName , info , compilerOptions , preferences ) ) ;
10
13
}
@@ -14,15 +17,15 @@ namespace ts.moduleSpecifiers {
14
17
moduleSymbol : Symbol ,
15
18
program : Program ,
16
19
importingSourceFile : SourceFile ,
17
- host : LanguageServiceHost ,
18
- preferences : UserPreferences ,
20
+ host : ModuleSpecifierResolutionHost ,
21
+ preferences : ModuleSpecifierPreferences ,
19
22
) : ReadonlyArray < ReadonlyArray < string > > {
20
23
const ambient = tryGetModuleNameFromAmbientModule ( moduleSymbol ) ;
21
24
if ( ambient ) return [ [ ambient ] ] ;
22
25
23
26
const compilerOptions = program . getCompilerOptions ( ) ;
24
27
const info = getInfo ( compilerOptions , importingSourceFile , importingSourceFile . fileName , host ) ;
25
- const modulePaths = getAllModulePaths ( program , moduleSymbol . valueDeclaration . getSourceFile ( ) ) ;
28
+ const modulePaths = getAllModulePaths ( program , getSourceFileOfNode ( moduleSymbol . valueDeclaration ) ) ;
26
29
27
30
const global = mapDefined ( modulePaths , moduleFileName => getGlobalModuleSpecifier ( moduleFileName , info , host , compilerOptions ) ) ;
28
31
return global . length ? global . map ( g => [ g ] ) : modulePaths . map ( moduleFileName =>
@@ -36,18 +39,18 @@ namespace ts.moduleSpecifiers {
36
39
readonly sourceDirectory : string ;
37
40
}
38
41
// importingSourceFileName is separate because getEditsForFileRename may need to specify an updated path
39
- function getInfo ( compilerOptions : CompilerOptions , importingSourceFile : SourceFile , importingSourceFileName : string , host : LanguageServiceHost ) : Info {
42
+ function getInfo ( compilerOptions : CompilerOptions , importingSourceFile : SourceFile , importingSourceFileName : string , host : ModuleSpecifierResolutionHost ) : Info {
40
43
const moduleResolutionKind = getEmitModuleResolutionKind ( compilerOptions ) ;
41
44
const addJsExtension = usesJsExtensionOnImports ( importingSourceFile ) ;
42
- const getCanonicalFileName = hostGetCanonicalFileName ( host ) ;
45
+ const getCanonicalFileName = createGetCanonicalFileName ( host . useCaseSensitiveFileNames ? host . useCaseSensitiveFileNames ( ) : true ) ;
43
46
const sourceDirectory = getDirectoryPath ( importingSourceFileName ) ;
44
47
return { moduleResolutionKind, addJsExtension, getCanonicalFileName, sourceDirectory } ;
45
48
}
46
49
47
50
function getGlobalModuleSpecifier (
48
51
moduleFileName : string ,
49
52
{ addJsExtension, getCanonicalFileName, sourceDirectory } : Info ,
50
- host : LanguageServiceHost ,
53
+ host : ModuleSpecifierResolutionHost ,
51
54
compilerOptions : CompilerOptions ,
52
55
) {
53
56
return tryGetModuleNameFromTypeRoots ( compilerOptions , host , getCanonicalFileName , moduleFileName , addJsExtension )
@@ -59,7 +62,7 @@ namespace ts.moduleSpecifiers {
59
62
moduleFileName : string ,
60
63
{ moduleResolutionKind, addJsExtension, getCanonicalFileName, sourceDirectory } : Info ,
61
64
compilerOptions : CompilerOptions ,
62
- preferences : UserPreferences ,
65
+ preferences : ModuleSpecifierPreferences ,
63
66
) {
64
67
const { baseUrl, paths } = compilerOptions ;
65
68
@@ -210,7 +213,7 @@ namespace ts.moduleSpecifiers {
210
213
function tryGetModuleNameAsNodeModule (
211
214
options : CompilerOptions ,
212
215
moduleFileName : string ,
213
- host : LanguageServiceHost ,
216
+ host : ModuleSpecifierResolutionHost ,
214
217
getCanonicalFileName : ( file : string ) => string ,
215
218
sourceDirectory : string ,
216
219
) : string | undefined {
@@ -256,14 +259,26 @@ namespace ts.moduleSpecifiers {
256
259
const fullModulePathWithoutExtension = removeFileExtension ( path ) ;
257
260
258
261
// If the file is /index, it can be imported by its directory name
259
- if ( getCanonicalFileName ( fullModulePathWithoutExtension . substring ( parts . fileNameIndex ) ) === "/index" ) {
262
+ // IFF there is not _also_ a file by the same name
263
+ if ( getCanonicalFileName ( fullModulePathWithoutExtension . substring ( parts . fileNameIndex ) ) === "/index" && ! tryGetAnyFileFromPath ( host , fullModulePathWithoutExtension . substring ( 0 , parts . fileNameIndex ) ) ) {
260
264
return fullModulePathWithoutExtension . substring ( 0 , parts . fileNameIndex ) ;
261
265
}
262
266
263
267
return fullModulePathWithoutExtension ;
264
268
}
265
269
}
266
270
271
+ function tryGetAnyFileFromPath ( host : ModuleSpecifierResolutionHost , path : string ) {
272
+ // We check all js, `node` and `json` extensions in addition to TS, since node module resolution would also choose those over the directory
273
+ const extensions = getSupportedExtensions ( { allowJs : true } , [ { extension : "node" , isMixedContent : false } , { extension : "json" , isMixedContent : false , scriptKind : ScriptKind . JSON } ] ) ;
274
+ for ( const e of extensions ) {
275
+ const fullPath = path + e ;
276
+ if ( host . fileExists ! ( fullPath ) ) {
277
+ return fullPath ;
278
+ }
279
+ }
280
+ }
281
+
267
282
interface NodeModulePathParts {
268
283
readonly topLevelNodeModulesIndex : number ;
269
284
readonly topLevelPackageNameIndex : number ;
0 commit comments