@@ -15,17 +15,20 @@ namespace ts.moduleSpecifiers {
15
15
// For each symlink/original for a module, returns a list of ways to import that file.
16
16
export function getModuleSpecifiers (
17
17
moduleSymbol : Symbol ,
18
- program : Program ,
18
+ compilerOptions : CompilerOptions ,
19
19
importingSourceFile : SourceFile ,
20
20
host : ModuleSpecifierResolutionHost ,
21
+ files : ReadonlyArray < SourceFile > ,
21
22
preferences : ModuleSpecifierPreferences ,
22
23
) : ReadonlyArray < ReadonlyArray < string > > {
23
24
const ambient = tryGetModuleNameFromAmbientModule ( moduleSymbol ) ;
24
25
if ( ambient ) return [ [ ambient ] ] ;
25
26
26
- const compilerOptions = program . getCompilerOptions ( ) ;
27
- const info = getInfo ( compilerOptions , importingSourceFile , importingSourceFile . fileName , host ) ;
28
- const modulePaths = getAllModulePaths ( program , getSourceFileOfNode ( moduleSymbol . valueDeclaration ) ) ;
27
+ const info = getInfo ( compilerOptions , importingSourceFile , importingSourceFile . path , host ) ;
28
+ if ( ! files ) {
29
+ return Debug . fail ( "Files list must be present to resolve symlinks in specifier resolution" ) ;
30
+ }
31
+ const modulePaths = getAllModulePaths ( files , getSourceFileOfNode ( moduleSymbol . valueDeclaration ) , info . getCanonicalFileName , host ) ;
29
32
30
33
const global = mapDefined ( modulePaths , moduleFileName => getGlobalModuleSpecifier ( moduleFileName , info , host , compilerOptions ) ) ;
31
34
return global . length ? global . map ( g => [ g ] ) : modulePaths . map ( moduleFileName =>
@@ -130,15 +133,57 @@ namespace ts.moduleSpecifiers {
130
133
return firstDefined ( imports , ( { text } ) => pathIsRelative ( text ) ? fileExtensionIs ( text , Extension . Js ) : undefined ) || false ;
131
134
}
132
135
136
+ function discoverProbableSymlinks ( files : ReadonlyArray < SourceFile > ) {
137
+ const symlinks = mapDefined ( files , sf =>
138
+ sf . resolvedModules && firstDefinedIterator ( sf . resolvedModules . values ( ) , res =>
139
+ res && res . originalPath && res . resolvedFileName !== res . originalPath ? [ res . resolvedFileName , res . originalPath ] : undefined ) ) ;
140
+ const result = createMap < string > ( ) ;
141
+ if ( symlinks ) {
142
+ for ( const [ resolvedPath , originalPath ] of symlinks ) {
143
+ const resolvedParts = getPathComponents ( resolvedPath ) ;
144
+ const originalParts = getPathComponents ( originalPath ) ;
145
+ while ( resolvedParts [ resolvedParts . length - 1 ] === originalParts [ originalParts . length - 1 ] ) {
146
+ resolvedParts . pop ( ) ;
147
+ originalParts . pop ( ) ;
148
+ }
149
+ result . set ( getPathFromPathComponents ( originalParts ) , getPathFromPathComponents ( resolvedParts ) ) ;
150
+ }
151
+ }
152
+ return result ;
153
+ }
154
+
155
+ function getAllModulePathsUsingIndirectSymlinks ( files : ReadonlyArray < SourceFile > , target : string , getCanonicalFileName : ( file : string ) => string , host : ModuleSpecifierResolutionHost ) {
156
+ const links = discoverProbableSymlinks ( files ) ;
157
+ const paths = arrayFrom ( links . keys ( ) ) ;
158
+ let options : string [ ] | undefined ;
159
+ for ( const path of paths ) {
160
+ const resolved = links . get ( path ) ! ;
161
+ if ( startsWith ( target , resolved + "/" ) ) {
162
+ const relative = getRelativePathFromDirectory ( resolved , target , getCanonicalFileName ) ;
163
+ const option = resolvePath ( path , relative ) ;
164
+ if ( ! host . fileExists || host . fileExists ( option ) ) {
165
+ if ( ! options ) options = [ ] ;
166
+ options . push ( option ) ;
167
+ }
168
+ }
169
+ }
170
+ const resolvedtarget = host . getCurrentDirectory ? resolvePath ( host . getCurrentDirectory ( ) , target ) : target ;
171
+ if ( options ) {
172
+ options . push ( resolvedtarget ) ; // Since these are speculative, we also include the original resolved name as a possibility
173
+ return options ;
174
+ }
175
+ return [ resolvedtarget ] ;
176
+ }
177
+
133
178
/**
134
179
* Looks for a existing imports that use symlinks to this module.
135
180
* Only if no symlink is available, the real path will be used.
136
181
*/
137
- function getAllModulePaths ( program : Program , { fileName } : SourceFile ) : ReadonlyArray < string > {
138
- const symlinks = mapDefined ( program . getSourceFiles ( ) , sf =>
182
+ function getAllModulePaths ( files : ReadonlyArray < SourceFile > , { fileName } : SourceFile , getCanonicalFileName : ( file : string ) => string , host : ModuleSpecifierResolutionHost ) : ReadonlyArray < string > {
183
+ const symlinks = mapDefined ( files , sf =>
139
184
sf . resolvedModules && firstDefinedIterator ( sf . resolvedModules . values ( ) , res =>
140
185
res && res . resolvedFileName === fileName ? res . originalPath : undefined ) ) ;
141
- return symlinks . length === 0 ? [ fileName ] : symlinks ;
186
+ return symlinks . length === 0 ? getAllModulePathsUsingIndirectSymlinks ( files , fileName , getCanonicalFileName , host ) : symlinks ;
142
187
}
143
188
144
189
function getRelativePathNParents ( relativePath : string ) : number {
0 commit comments