@@ -12,6 +12,7 @@ namespace ts {
12
12
resolveTypeReferenceDirectives ( typeDirectiveNames : string [ ] , containingFile : string ) : ResolvedTypeReferenceDirective [ ] ;
13
13
14
14
invalidateResolutionOfFile ( filePath : Path ) : void ;
15
+ removeResolutionsOfFile ( filePath : Path ) : void ;
15
16
createHasInvalidatedResolution ( ) : HasInvalidatedResolution ;
16
17
17
18
startCachingPerDirectoryResolution ( ) : void ;
@@ -25,7 +26,7 @@ namespace ts {
25
26
clear ( ) : void ;
26
27
}
27
28
28
- interface NameResolutionWithFailedLookupLocations {
29
+ interface ResolutionWithFailedLookupLocations {
29
30
readonly failedLookupLocations : ReadonlyArray < string > ;
30
31
isInvalidated ?: boolean ;
31
32
}
@@ -45,6 +46,7 @@ namespace ts {
45
46
projectName ?: string ;
46
47
getGlobalCache ?( ) : string | undefined ;
47
48
writeLog ( s : string ) : void ;
49
+ maxNumberOfFilesToIterateForInvalidation ?: number ;
48
50
}
49
51
50
52
interface DirectoryWatchesOfFailedLookup {
@@ -54,9 +56,17 @@ namespace ts {
54
56
refCount : number ;
55
57
}
56
58
59
+ /*@internal */
60
+ export const maxNumberOfFilesToIterateForInvalidation = 256 ;
61
+
62
+ interface GetResolutionWithResolvedFileName < T extends ResolutionWithFailedLookupLocations = ResolutionWithFailedLookupLocations , R extends ResolutionWithResolvedFileName = ResolutionWithResolvedFileName > {
63
+ ( resolution : T ) : R ;
64
+ }
65
+
57
66
export function createResolutionCache ( resolutionHost : ResolutionCacheHost ) : ResolutionCache {
58
67
let filesWithChangedSetOfUnresolvedImports : Path [ ] | undefined ;
59
68
let filesWithInvalidatedResolutions : Map < true > | undefined ;
69
+ let allFilesHaveInvalidatedResolution = false ;
60
70
61
71
// The resolvedModuleNames and resolvedTypeReferenceDirectives are the cache of resolutions per file.
62
72
// The key in the map is source file's path.
@@ -86,6 +96,7 @@ namespace ts {
86
96
finishCachingPerDirectoryResolution,
87
97
resolveModuleNames,
88
98
resolveTypeReferenceDirectives,
99
+ removeResolutionsOfFile,
89
100
invalidateResolutionOfFile,
90
101
createHasInvalidatedResolution,
91
102
setRootDirectory,
@@ -121,6 +132,7 @@ namespace ts {
121
132
closeTypeRootsWatch ( ) ;
122
133
resolvedModuleNames . clear ( ) ;
123
134
resolvedTypeReferenceDirectives . clear ( ) ;
135
+ allFilesHaveInvalidatedResolution = false ;
124
136
Debug . assert ( perDirectoryResolvedModuleNames . size === 0 && perDirectoryResolvedTypeReferenceDirectives . size === 0 ) ;
125
137
}
126
138
@@ -135,6 +147,11 @@ namespace ts {
135
147
}
136
148
137
149
function createHasInvalidatedResolution ( ) : HasInvalidatedResolution {
150
+ if ( allFilesHaveInvalidatedResolution ) {
151
+ // Any file asked would have invalidated resolution
152
+ filesWithInvalidatedResolutions = undefined ;
153
+ return returnTrue ;
154
+ }
138
155
const collected = filesWithInvalidatedResolutions ;
139
156
filesWithInvalidatedResolutions = undefined ;
140
157
return path => collected && collected . has ( path ) ;
@@ -145,6 +162,7 @@ namespace ts {
145
162
}
146
163
147
164
function finishCachingPerDirectoryResolution ( ) {
165
+ allFilesHaveInvalidatedResolution = false ;
148
166
directoryWatchesOfFailedLookups . forEach ( ( watcher , path ) => {
149
167
if ( watcher . refCount === 0 ) {
150
168
directoryWatchesOfFailedLookups . delete ( path ) ;
@@ -178,13 +196,13 @@ namespace ts {
178
196
return primaryResult ;
179
197
}
180
198
181
- function resolveNamesWithLocalCache < T extends NameResolutionWithFailedLookupLocations , R extends ResolutionWithResolvedFileName > (
199
+ function resolveNamesWithLocalCache < T extends ResolutionWithFailedLookupLocations , R extends ResolutionWithResolvedFileName > (
182
200
names : string [ ] ,
183
201
containingFile : string ,
184
202
cache : Map < Map < T > > ,
185
203
perDirectoryCache : Map < Map < T > > ,
186
204
loader : ( name : string , containingFile : string , options : CompilerOptions , host : ModuleResolutionHost ) => T ,
187
- getResolutionFromNameResolutionWithFailedLookupLocations : ( s : T ) => R ,
205
+ getResolutionWithResolvedFileName : GetResolutionWithResolvedFileName < T , R > ,
188
206
logChanges : boolean ) : R [ ] {
189
207
190
208
const path = resolutionHost . toPath ( containingFile ) ;
@@ -203,7 +221,7 @@ namespace ts {
203
221
for ( const name of names ) {
204
222
let resolution = resolutionsInFile . get ( name ) ;
205
223
// Resolution is valid if it is present and not invalidated
206
- if ( ! resolution || resolution . isInvalidated ) {
224
+ if ( allFilesHaveInvalidatedResolution || ! resolution || resolution . isInvalidated ) {
207
225
const existingResolution = resolution ;
208
226
const resolutionInDirectory = perDirectoryResolution . get ( name ) ;
209
227
if ( resolutionInDirectory ) {
@@ -225,7 +243,7 @@ namespace ts {
225
243
}
226
244
Debug . assert ( resolution !== undefined && ! resolution . isInvalidated ) ;
227
245
seenNamesInFile . set ( name , true ) ;
228
- resolvedModules . push ( getResolutionFromNameResolutionWithFailedLookupLocations ( resolution ) ) ;
246
+ resolvedModules . push ( getResolutionWithResolvedFileName ( resolution ) ) ;
229
247
}
230
248
231
249
// Stop watching and remove the unused name
@@ -245,8 +263,8 @@ namespace ts {
245
263
if ( ! oldResolution || ! newResolution || oldResolution . isInvalidated ) {
246
264
return false ;
247
265
}
248
- const oldResult = getResolutionFromNameResolutionWithFailedLookupLocations ( oldResolution ) ;
249
- const newResult = getResolutionFromNameResolutionWithFailedLookupLocations ( newResolution ) ;
266
+ const oldResult = getResolutionWithResolvedFileName ( oldResolution ) ;
267
+ const newResult = getResolutionWithResolvedFileName ( newResolution ) ;
250
268
if ( oldResult === newResult ) {
251
269
return true ;
252
270
}
@@ -321,9 +339,7 @@ namespace ts {
321
339
return fileExtensionIsOneOf ( path , failedLookupDefaultExtensions ) ;
322
340
}
323
341
324
- function watchFailedLookupLocationOfResolution < T extends NameResolutionWithFailedLookupLocations > (
325
- resolution : T , startIndex ?: number
326
- ) {
342
+ function watchFailedLookupLocationOfResolution ( resolution : ResolutionWithFailedLookupLocations , startIndex ?: number ) {
327
343
if ( resolution && resolution . failedLookupLocations ) {
328
344
for ( let i = startIndex || 0 ; i < resolution . failedLookupLocations . length ; i ++ ) {
329
345
const failedLookupLocation = resolution . failedLookupLocations [ i ] ;
@@ -346,15 +362,11 @@ namespace ts {
346
362
}
347
363
}
348
364
349
- function stopWatchFailedLookupLocationOfResolution < T extends NameResolutionWithFailedLookupLocations > (
350
- resolution : T
351
- ) {
365
+ function stopWatchFailedLookupLocationOfResolution ( resolution : ResolutionWithFailedLookupLocations ) {
352
366
stopWatchFailedLookupLocationOfResolutionFrom ( resolution , 0 ) ;
353
367
}
354
368
355
- function stopWatchFailedLookupLocationOfResolutionFrom < T extends NameResolutionWithFailedLookupLocations > (
356
- resolution : T , startIndex : number
357
- ) {
369
+ function stopWatchFailedLookupLocationOfResolutionFrom ( resolution : ResolutionWithFailedLookupLocations , startIndex : number ) {
358
370
if ( resolution && resolution . failedLookupLocations ) {
359
371
for ( let i = startIndex ; i < resolution . failedLookupLocations . length ; i ++ ) {
360
372
const failedLookupLocation = resolution . failedLookupLocations [ i ] ;
@@ -387,107 +399,106 @@ namespace ts {
387
399
// If the files are added to project root or node_modules directory, always run through the invalidation process
388
400
// Otherwise run through invalidation only if adding to the immediate directory
389
401
if ( dirPath === rootPath || isNodeModulesDirectory ( dirPath ) || getDirectoryPath ( fileOrFolderPath ) === dirPath ) {
390
- let isChangedFailedLookupLocation : ( location : string ) => boolean ;
391
- if ( dirPath === fileOrFolderPath ) {
392
- // Watching directory is created
393
- // Invalidate any resolution has failed lookup in this directory
394
- isChangedFailedLookupLocation = location => isInDirectoryPath ( dirPath , resolutionHost . toPath ( location ) ) ;
395
- }
396
- else {
397
- // Some file or folder in the watching directory is created
398
- // Return early if it does not have any of the watching extension or not the custom failed lookup path
399
- if ( ! isPathWithDefaultFailedLookupExtension ( fileOrFolderPath ) && ! customFailedLookupPaths . has ( fileOrFolderPath ) ) {
400
- return ;
401
- }
402
- // Resolution need to be invalidated if failed lookup location is same as the file or folder getting created
403
- isChangedFailedLookupLocation = location => resolutionHost . toPath ( location ) === fileOrFolderPath ;
404
- }
405
- const hasChangedFailedLookupLocation = ( resolution : NameResolutionWithFailedLookupLocations ) => some ( resolution . failedLookupLocations , isChangedFailedLookupLocation ) ;
406
- if ( invalidateResolutionOfFailedLookupLocation ( hasChangedFailedLookupLocation ) ) {
402
+ if ( invalidateResolutionOfFailedLookupLocation ( fileOrFolderPath , dirPath === fileOrFolderPath ) ) {
407
403
resolutionHost . onInvalidatedResolution ( ) ;
408
404
}
409
405
}
410
406
} , WatchDirectoryFlags . Recursive ) ;
411
407
}
412
408
413
- function invalidateResolutionCache < T extends NameResolutionWithFailedLookupLocations > (
409
+ function removeResolutionsOfFileFromCache ( cache : Map < Map < ResolutionWithFailedLookupLocations > > , filePath : Path ) {
410
+ // Deleted file, stop watching failed lookups for all the resolutions in the file
411
+ const resolutions = cache . get ( filePath ) ;
412
+ if ( resolutions ) {
413
+ resolutions . forEach ( stopWatchFailedLookupLocationOfResolution ) ;
414
+ cache . delete ( filePath ) ;
415
+ }
416
+ }
417
+
418
+ function removeResolutionsOfFile ( filePath : Path ) {
419
+ removeResolutionsOfFileFromCache ( resolvedModuleNames , filePath ) ;
420
+ removeResolutionsOfFileFromCache ( resolvedTypeReferenceDirectives , filePath ) ;
421
+ }
422
+
423
+ function invalidateResolutionCache < T extends ResolutionWithFailedLookupLocations , R extends ResolutionWithResolvedFileName > (
414
424
cache : Map < Map < T > > ,
415
- ignoreFile : ( resolutions : Map < T > , containingFilePath : Path ) => boolean ,
416
- isInvalidatedResolution : ( resolution : T ) => boolean
425
+ isInvalidatedResolution : ( resolution : T , getResolutionWithResolvedFileName : GetResolutionWithResolvedFileName < T , R > ) => boolean ,
426
+ getResolutionWithResolvedFileName : GetResolutionWithResolvedFileName < T , R >
417
427
) {
418
428
const seen = createMap < Map < true > > ( ) ;
419
429
cache . forEach ( ( resolutions , containingFilePath ) => {
420
- if ( ! ignoreFile ( resolutions , containingFilePath as Path ) && resolutions ) {
421
- const dirPath = getDirectoryPath ( containingFilePath ) ;
422
- let seenInDir = seen . get ( dirPath ) ;
423
- if ( ! seenInDir ) {
424
- seenInDir = createMap < true > ( ) ;
425
- seen . set ( dirPath , seenInDir ) ;
426
- }
427
- resolutions . forEach ( ( resolution , name ) => {
428
- if ( seenInDir . has ( name ) ) {
429
- return ;
430
- }
431
- seenInDir . set ( name , true ) ;
432
- if ( ! resolution . isInvalidated && isInvalidatedResolution ( resolution ) ) {
433
- // Mark the file as needing re-evaluation of module resolution instead of using it blindly.
434
- resolution . isInvalidated = true ;
435
- ( filesWithInvalidatedResolutions || ( filesWithInvalidatedResolutions = createMap < true > ( ) ) ) . set ( containingFilePath , true ) ;
436
- }
437
- } ) ;
430
+ const dirPath = getDirectoryPath ( containingFilePath ) ;
431
+ let seenInDir = seen . get ( dirPath ) ;
432
+ if ( ! seenInDir ) {
433
+ seenInDir = createMap < true > ( ) ;
434
+ seen . set ( dirPath , seenInDir ) ;
438
435
}
436
+ resolutions . forEach ( ( resolution , name ) => {
437
+ if ( seenInDir . has ( name ) ) {
438
+ return ;
439
+ }
440
+ seenInDir . set ( name , true ) ;
441
+ if ( ! resolution . isInvalidated && isInvalidatedResolution ( resolution , getResolutionWithResolvedFileName ) ) {
442
+ // Mark the file as needing re-evaluation of module resolution instead of using it blindly.
443
+ resolution . isInvalidated = true ;
444
+ ( filesWithInvalidatedResolutions || ( filesWithInvalidatedResolutions = createMap < true > ( ) ) ) . set ( containingFilePath , true ) ;
445
+ }
446
+ } ) ;
439
447
} ) ;
440
448
}
441
449
442
- function invalidateResolutionCacheOfDeletedFile < T extends NameResolutionWithFailedLookupLocations , R extends ResolutionWithResolvedFileName > (
443
- deletedFilePath : Path ,
444
- cache : Map < Map < T > > ,
445
- getResolutionFromNameResolutionWithFailedLookupLocations : ( s : T ) => R ,
450
+ function hasReachedResolutionIterationLimit ( ) {
451
+ const maxSize = resolutionHost . maxNumberOfFilesToIterateForInvalidation || maxNumberOfFilesToIterateForInvalidation ;
452
+ return resolvedModuleNames . size > maxSize || resolvedTypeReferenceDirectives . size > maxSize ;
453
+ }
454
+
455
+ function invalidateResolutions (
456
+ isInvalidatedResolution : ( resolution : ResolutionWithFailedLookupLocations , getResolutionWithResolvedFileName : GetResolutionWithResolvedFileName ) => boolean ,
446
457
) {
447
- invalidateResolutionCache (
448
- cache ,
449
- // Ignore file thats same as deleted file path, and handle it here
450
- ( resolutions , containingFilePath ) => {
451
- if ( containingFilePath !== deletedFilePath ) {
452
- return false ;
453
- }
458
+ // If more than maxNumberOfFilesToIterateForInvalidation present,
459
+ // just invalidated all files and recalculate the resolutions for files instead
460
+ if ( hasReachedResolutionIterationLimit ( ) ) {
461
+ allFilesHaveInvalidatedResolution = true ;
462
+ return ;
463
+ }
464
+ invalidateResolutionCache ( resolvedModuleNames , isInvalidatedResolution , getResolvedModule ) ;
465
+ invalidateResolutionCache ( resolvedTypeReferenceDirectives , isInvalidatedResolution , getResolvedTypeReferenceDirective ) ;
466
+ }
454
467
455
- // Deleted file, stop watching failed lookups for all the resolutions in the file
456
- cache . delete ( containingFilePath ) ;
457
- resolutions . forEach ( stopWatchFailedLookupLocationOfResolution ) ;
458
- return true ;
459
- } ,
468
+ function invalidateResolutionOfFile ( filePath : Path ) {
469
+ removeResolutionsOfFile ( filePath ) ;
470
+ invalidateResolutions (
460
471
// Resolution is invalidated if the resulting file name is same as the deleted file path
461
- resolution => {
462
- const result = getResolutionFromNameResolutionWithFailedLookupLocations ( resolution ) ;
463
- return result && resolutionHost . toPath ( result . resolvedFileName ) === deletedFilePath ;
472
+ ( resolution , getResolutionWithResolvedFileName ) => {
473
+ const result = getResolutionWithResolvedFileName ( resolution ) ;
474
+ return result && resolutionHost . toPath ( result . resolvedFileName ) === filePath ;
464
475
}
465
476
) ;
466
477
}
467
478
468
- function invalidateResolutionOfFile ( filePath : Path ) {
469
- invalidateResolutionCacheOfDeletedFile ( filePath , resolvedModuleNames , getResolvedModule ) ;
470
- invalidateResolutionCacheOfDeletedFile ( filePath , resolvedTypeReferenceDirectives , getResolvedTypeReferenceDirective ) ;
471
- }
472
-
473
- function invalidateResolutionCacheOfFailedLookupLocation < T extends NameResolutionWithFailedLookupLocations > (
474
- cache : Map < Map < T > > ,
475
- hasChangedFailedLookupLocation : ( resolution : T ) => boolean
476
- ) {
477
- invalidateResolutionCache (
478
- cache ,
479
- // Do not ignore any file
480
- returnFalse ,
479
+ function invalidateResolutionOfFailedLookupLocation ( fileOrFolderPath : Path , isCreatingWatchedDirectory : boolean ) {
480
+ let isChangedFailedLookupLocation : ( location : string ) => boolean ;
481
+ if ( isCreatingWatchedDirectory ) {
482
+ // Watching directory is created
483
+ // Invalidate any resolution has failed lookup in this directory
484
+ isChangedFailedLookupLocation = location => isInDirectoryPath ( fileOrFolderPath , resolutionHost . toPath ( location ) ) ;
485
+ }
486
+ else {
487
+ // Some file or folder in the watching directory is created
488
+ // Return early if it does not have any of the watching extension or not the custom failed lookup path
489
+ if ( ! isPathWithDefaultFailedLookupExtension ( fileOrFolderPath ) && ! customFailedLookupPaths . has ( fileOrFolderPath ) ) {
490
+ return false ;
491
+ }
492
+ // Resolution need to be invalidated if failed lookup location is same as the file or folder getting created
493
+ isChangedFailedLookupLocation = location => resolutionHost . toPath ( location ) === fileOrFolderPath ;
494
+ }
495
+ const hasChangedFailedLookupLocation = ( resolution : ResolutionWithFailedLookupLocations ) => some ( resolution . failedLookupLocations , isChangedFailedLookupLocation ) ;
496
+ const invalidatedFilesCount = filesWithInvalidatedResolutions && filesWithInvalidatedResolutions . size ;
497
+ invalidateResolutions (
481
498
// Resolution is invalidated if the resulting file name is same as the deleted file path
482
499
hasChangedFailedLookupLocation
483
500
) ;
484
- }
485
-
486
- function invalidateResolutionOfFailedLookupLocation ( hasChangedFailedLookupLocation : ( resolution : NameResolutionWithFailedLookupLocations ) => boolean ) {
487
- const invalidatedFilesCount = filesWithInvalidatedResolutions && filesWithInvalidatedResolutions . size ;
488
- invalidateResolutionCacheOfFailedLookupLocation ( resolvedModuleNames , hasChangedFailedLookupLocation ) ;
489
- invalidateResolutionCacheOfFailedLookupLocation ( resolvedTypeReferenceDirectives , hasChangedFailedLookupLocation ) ;
490
- return filesWithInvalidatedResolutions && filesWithInvalidatedResolutions . size !== invalidatedFilesCount ;
501
+ return allFilesHaveInvalidatedResolution || filesWithInvalidatedResolutions && filesWithInvalidatedResolutions . size !== invalidatedFilesCount ;
491
502
}
492
503
493
504
function closeTypeRootsWatch ( ) {
0 commit comments