@@ -43,6 +43,13 @@ namespace ts {
43
43
writeLog ( s : string ) : void ;
44
44
}
45
45
46
+ interface DirectoryWatchesOfFailedLookup {
47
+ /** watcher for the directory of failed lookup */
48
+ watcher : FileWatcher ;
49
+ /** ref count keeping this directory watch alive */
50
+ refCount : number ;
51
+ }
52
+
46
53
export function createResolutionCache ( resolutionHost : ResolutionCacheHost ) : ResolutionCache {
47
54
let filesWithChangedSetOfUnresolvedImports : Path [ ] | undefined ;
48
55
let filesWithInvalidatedResolutions : Map < true > | undefined ;
@@ -56,8 +63,7 @@ namespace ts {
56
63
const perDirectoryResolvedTypeReferenceDirectives = createMap < Map < ResolvedTypeReferenceDirectiveWithFailedLookupLocations > > ( ) ;
57
64
const getCurrentDirectory = memoize ( ( ) => resolutionHost . getCurrentDirectory ( ) ) ;
58
65
59
- const directoryWatchesOfFailedLookups = createMap < FileWatcher > ( ) ;
60
- let hasChangesInFailedLookupLocations = false ;
66
+ const directoryWatchesOfFailedLookups = createMap < DirectoryWatchesOfFailedLookup > ( ) ;
61
67
let rootDir : string ;
62
68
let rootPath : Path ;
63
69
@@ -93,8 +99,7 @@ namespace ts {
93
99
}
94
100
95
101
function clear ( ) {
96
- clearMap ( directoryWatchesOfFailedLookups , closeFileWatcher ) ;
97
- hasChangesInFailedLookupLocations = false ;
102
+ clearMap ( directoryWatchesOfFailedLookups , closeFileWatcherOf ) ;
98
103
closeTypeRootsWatch ( ) ;
99
104
resolvedModuleNames . clear ( ) ;
100
105
resolvedTypeReferenceDirectives . clear ( ) ;
@@ -122,17 +127,12 @@ namespace ts {
122
127
}
123
128
124
129
function finishCachingPerDirectoryResolution ( ) {
125
- if ( hasChangesInFailedLookupLocations ) {
126
- const seenDirectories = createMap < true > ( ) ;
127
- watchFailedLookupLocationForCache ( perDirectoryResolvedModuleNames , seenDirectories ) ;
128
- watchFailedLookupLocationForCache ( perDirectoryResolvedTypeReferenceDirectives , seenDirectories ) ;
129
- directoryWatchesOfFailedLookups . forEach ( ( watcher , path ) => {
130
- if ( ! seenDirectories . has ( path ) ) {
131
- watcher . close ( ) ;
132
- }
133
- } ) ;
134
- hasChangesInFailedLookupLocations = false ;
135
- }
130
+ directoryWatchesOfFailedLookups . forEach ( ( watcher , path ) => {
131
+ if ( watcher . refCount === 0 ) {
132
+ directoryWatchesOfFailedLookups . delete ( path ) ;
133
+ watcher . watcher . close ( ) ;
134
+ }
135
+ } ) ;
136
136
137
137
perDirectoryResolvedModuleNames . clear ( ) ;
138
138
perDirectoryResolvedTypeReferenceDirectives . clear ( ) ;
@@ -171,61 +171,75 @@ namespace ts {
171
171
logChanges : boolean ) : R [ ] {
172
172
173
173
const path = resolutionHost . toPath ( containingFile ) ;
174
- const currentResolutionsInFile = cache . get ( path ) ;
174
+ const resolutionsInFile = cache . get ( path ) || cache . set ( path , createMap ( ) ) . get ( path ) ;
175
175
const dirPath = getDirectoryPath ( path ) ;
176
176
let perDirectoryResolution = perDirectoryCache . get ( dirPath ) ;
177
177
if ( ! perDirectoryResolution ) {
178
178
perDirectoryResolution = createMap ( ) ;
179
179
perDirectoryCache . set ( dirPath , perDirectoryResolution ) ;
180
180
}
181
181
182
- const newResolutions : Map < T > = createMap < T > ( ) ;
183
182
const resolvedModules : R [ ] = [ ] ;
184
183
const compilerOptions = resolutionHost . getCompilationSettings ( ) ;
185
184
185
+ const seenNamesInFile = createMap < true > ( ) ;
186
186
for ( const name of names ) {
187
187
// check if this is a duplicate entry in the list
188
- let resolution = newResolutions . get ( name ) ;
189
- if ( ! resolution ) {
190
- const existingResolution = currentResolutionsInFile && currentResolutionsInFile . get ( name ) ;
191
- if ( existingResolution ) {
192
- // Remove from the cache since we would update the resolution in new file ourselves
193
- currentResolutionsInFile . delete ( name ) ;
194
- }
195
-
196
- if ( moduleResolutionIsValid ( existingResolution ) ) {
197
- // ok, it is safe to use existing name resolution results
198
- resolution = existingResolution ;
188
+ let resolution = resolutionsInFile . get ( name ) ;
189
+ if ( ! moduleResolutionIsValid ( resolution , name ) ) {
190
+ const existingResolution = resolution ;
191
+ const resolutionInDirectory = perDirectoryResolution . get ( name ) ;
192
+ if ( resolutionInDirectory ) {
193
+ resolution = resolutionInDirectory ;
199
194
}
200
195
else {
201
- const resolutionInDirectory = perDirectoryResolution && perDirectoryResolution . get ( name ) ;
202
- if ( resolutionInDirectory ) {
203
- resolution = resolutionInDirectory ;
204
- }
205
- else {
206
- resolution = loader ( name , containingFile , compilerOptions , resolutionHost ) ;
207
- perDirectoryResolution . set ( name , resolution ) ;
208
- hasChangesInFailedLookupLocations = hasChangesInFailedLookupLocations ||
209
- resolution . failedLookupLocations !== ( existingResolution && existingResolution . failedLookupLocations ) ;
210
- }
196
+ resolution = loader ( name , containingFile , compilerOptions , resolutionHost ) ;
197
+ perDirectoryResolution . set ( name , resolution ) ;
211
198
}
212
- newResolutions . set ( name , resolution ) ;
199
+ resolutionsInFile . set ( name , resolution ) ;
200
+ const diffIndex = existingResolution && existingResolution . failedLookupLocations && resolution . failedLookupLocations && findDiffIndex ( resolution . failedLookupLocations , existingResolution . failedLookupLocations ) ;
201
+ watchFailedLookupLocationOfResolution ( resolution , diffIndex || 0 ) ;
202
+ stopWatchFailedLookupLocationOfResolutionFrom ( existingResolution , diffIndex || 0 ) ;
213
203
if ( logChanges && filesWithChangedSetOfUnresolvedImports && ! resolutionIsEqualTo ( existingResolution , resolution ) ) {
214
204
filesWithChangedSetOfUnresolvedImports . push ( path ) ;
215
205
// reset log changes to avoid recording the same file multiple times
216
206
logChanges = false ;
217
207
}
218
208
}
219
-
220
- Debug . assert ( resolution !== undefined ) ;
221
-
209
+ Debug . assert ( resolution !== undefined && ! resolution . isInvalidated ) ;
210
+ seenNamesInFile . set ( name , true ) ;
222
211
resolvedModules . push ( getResult ( resolution ) ) ;
223
212
}
224
213
225
- // replace old results with a new one
226
- cache . set ( path , newResolutions ) ;
214
+ // Stop watching and remove the unused name
215
+ resolutionsInFile . forEach ( ( resolution , name ) => {
216
+ if ( ! seenNamesInFile . has ( name ) ) {
217
+ stopWatchFailedLookupLocationOfResolution ( resolution ) ;
218
+ resolutionsInFile . delete ( name ) ;
219
+ }
220
+ } ) ;
221
+
227
222
return resolvedModules ;
228
223
224
+ function moduleResolutionIsValid ( resolution : T , name : string ) : boolean {
225
+ // This is already calculated resolution in this round of synchronization
226
+ if ( seenNamesInFile . has ( name ) ) {
227
+ return true ;
228
+ }
229
+
230
+ if ( ! resolution || resolution . isInvalidated ) {
231
+ return false ;
232
+ }
233
+
234
+ const result = getResult ( resolution ) ;
235
+ if ( result ) {
236
+ return true ;
237
+ }
238
+ // consider situation if we have no candidate locations as valid resolution.
239
+ // after all there is no point to invalidate it if we have no idea where to look for the module.
240
+ return resolution . failedLookupLocations . length === 0 ;
241
+ }
242
+
229
243
function resolutionIsEqualTo ( oldResolution : T , newResolution : T ) : boolean {
230
244
if ( oldResolution === newResolution ) {
231
245
return true ;
@@ -243,25 +257,9 @@ namespace ts {
243
257
}
244
258
return getResultFileName ( oldResult ) === getResultFileName ( newResult ) ;
245
259
}
246
-
247
- function moduleResolutionIsValid ( resolution : T ) : boolean {
248
- if ( ! resolution || resolution . isInvalidated ) {
249
- return false ;
250
- }
251
-
252
- const result = getResult ( resolution ) ;
253
- if ( result ) {
254
- return true ;
255
- }
256
-
257
- // consider situation if we have no candidate locations as valid resolution.
258
- // after all there is no point to invalidate it if we have no idea where to look for the module.
259
- return resolution . failedLookupLocations . length === 0 ;
260
- }
261
260
}
262
261
263
262
function resolveTypeReferenceDirectives ( typeDirectiveNames : string [ ] , containingFile : string ) : ResolvedTypeReferenceDirective [ ] {
264
- resolutionHost . writeLog ( `resolveTypeReferenceDirectives[${ resolvedTypeReferenceDirectives . size } "]: " + ${ containingFile } ` ) ;
265
263
return resolveNamesWithLocalCache (
266
264
typeDirectiveNames , containingFile ,
267
265
resolvedTypeReferenceDirectives , perDirectoryResolvedTypeReferenceDirectives ,
@@ -271,7 +269,6 @@ namespace ts {
271
269
}
272
270
273
271
function resolveModuleNames ( moduleNames : string [ ] , containingFile : string , logChanges : boolean ) : ResolvedModuleFull [ ] {
274
- resolutionHost . writeLog ( `resolveModuleNames[${ resolvedModuleNames . size } "]: " + ${ containingFile } ` ) ;
275
272
return resolveNamesWithLocalCache (
276
273
moduleNames , containingFile ,
277
274
resolvedModuleNames , perDirectoryResolvedModuleNames ,
@@ -322,22 +319,42 @@ namespace ts {
322
319
return { dir, dirPath } ;
323
320
}
324
321
325
- function watchFailedLookupLocationForCache < T extends NameResolutionWithFailedLookupLocations > (
326
- cache : Map < Map < T > > , seenDirectories : Map < true >
322
+ function watchFailedLookupLocationOfResolution < T extends NameResolutionWithFailedLookupLocations > (
323
+ resolution : T , startIndex ?: number
327
324
) {
328
- cache . forEach ( value => value && value . forEach (
329
- resolution => forEach ( resolution && resolution . failedLookupLocations ,
330
- failedLookupLocation => {
331
- const { dir, dirPath } = getDirectoryToWatchFailedLookupLocation ( failedLookupLocation , resolutionHost . toPath ( failedLookupLocation ) ) ;
332
- if ( ! seenDirectories . has ( dirPath ) ) {
333
- if ( ! directoryWatchesOfFailedLookups . has ( dirPath ) ) {
334
- directoryWatchesOfFailedLookups . set ( dirPath , createDirectoryWatcher ( dir , dirPath ) ) ;
335
- }
336
- seenDirectories . set ( dirPath , true ) ;
337
- }
325
+ if ( resolution && resolution . failedLookupLocations ) {
326
+ for ( let i = startIndex || 0 ; i < resolution . failedLookupLocations . length ; i ++ ) {
327
+ const failedLookupLocation = resolution . failedLookupLocations [ i ] ;
328
+ const { dir, dirPath } = getDirectoryToWatchFailedLookupLocation ( failedLookupLocation , resolutionHost . toPath ( failedLookupLocation ) ) ;
329
+ const dirWatcher = directoryWatchesOfFailedLookups . get ( dirPath ) ;
330
+ if ( dirWatcher ) {
331
+ dirWatcher . refCount ++ ;
332
+ }
333
+ else {
334
+ directoryWatchesOfFailedLookups . set ( dirPath , { watcher : createDirectoryWatcher ( dir , dirPath ) , refCount : 1 } ) ;
338
335
}
339
- )
340
- ) ) ;
336
+ }
337
+ }
338
+ }
339
+
340
+ function stopWatchFailedLookupLocationOfResolution < T extends NameResolutionWithFailedLookupLocations > (
341
+ resolution : T
342
+ ) {
343
+ stopWatchFailedLookupLocationOfResolutionFrom ( resolution , 0 ) ;
344
+ }
345
+
346
+ function stopWatchFailedLookupLocationOfResolutionFrom < T extends NameResolutionWithFailedLookupLocations > (
347
+ resolution : T , startIndex : number
348
+ ) {
349
+ if ( resolution && resolution . failedLookupLocations ) {
350
+ for ( let i = startIndex ; i < resolution . failedLookupLocations . length ; i ++ ) {
351
+ const failedLookupLocation = resolution . failedLookupLocations [ i ] ;
352
+ const { dirPath } = getDirectoryToWatchFailedLookupLocation ( failedLookupLocation , resolutionHost . toPath ( failedLookupLocation ) ) ;
353
+ const dirWatcher = directoryWatchesOfFailedLookups . get ( dirPath ) ;
354
+ // Do not close the watcher yet since it might be needed by other failed lookup locations.
355
+ dirWatcher . refCount -- ;
356
+ }
357
+ }
341
358
}
342
359
343
360
function createDirectoryWatcher ( directory : string , dirPath : Path ) {
@@ -371,8 +388,9 @@ namespace ts {
371
388
cache . forEach ( ( value , path ) => {
372
389
if ( path === deletedFilePath ) {
373
390
cache . delete ( path ) ;
374
- hasChangesInFailedLookupLocations = hasChangesInFailedLookupLocations ||
375
- value && forEachEntry ( value , resolution => ! ! resolution . failedLookupLocations ) ;
391
+ if ( value ) {
392
+ value . forEach ( stopWatchFailedLookupLocationOfResolution ) ;
393
+ }
376
394
}
377
395
else if ( value ) {
378
396
value . forEach ( resolution => {
0 commit comments