@@ -30,6 +30,10 @@ namespace ts {
30
30
isInvalidated ?: boolean ;
31
31
}
32
32
33
+ interface ResolutionWithResolvedFileName {
34
+ resolvedFileName : string | undefined ;
35
+ }
36
+
33
37
export interface ResolutionCacheHost extends ModuleResolutionHost {
34
38
toPath ( fileName : string ) : Path ;
35
39
getCompilationSettings ( ) : CompilerOptions ;
@@ -59,10 +63,15 @@ namespace ts {
59
63
// The values are Map of resolutions with key being name lookedup.
60
64
const resolvedModuleNames = createMap < Map < ResolvedModuleWithFailedLookupLocations > > ( ) ;
61
65
const perDirectoryResolvedModuleNames = createMap < Map < ResolvedModuleWithFailedLookupLocations > > ( ) ;
66
+
62
67
const resolvedTypeReferenceDirectives = createMap < Map < ResolvedTypeReferenceDirectiveWithFailedLookupLocations > > ( ) ;
63
68
const perDirectoryResolvedTypeReferenceDirectives = createMap < Map < ResolvedTypeReferenceDirectiveWithFailedLookupLocations > > ( ) ;
69
+
64
70
const getCurrentDirectory = memoize ( ( ) => resolutionHost . getCurrentDirectory ( ) ) ;
65
71
72
+ const failedLookupDefaultExtensions = [ Extension . Ts , Extension . Tsx , Extension . Js , Extension . Jsx , Extension . Json ] ;
73
+ const customFailedLookupPaths = createMap < number > ( ) ;
74
+
66
75
const directoryWatchesOfFailedLookups = createMap < DirectoryWatchesOfFailedLookup > ( ) ;
67
76
let rootDir : string ;
68
77
let rootPath : Path ;
@@ -85,6 +94,14 @@ namespace ts {
85
94
clear
86
95
} ;
87
96
97
+ function getResolvedModule ( resolution : ResolvedModuleWithFailedLookupLocations ) {
98
+ return resolution . resolvedModule ;
99
+ }
100
+
101
+ function getResolvedTypeReferenceDirective ( resolution : ResolvedTypeReferenceDirectiveWithFailedLookupLocations ) {
102
+ return resolution . resolvedTypeReferenceDirective ;
103
+ }
104
+
88
105
function setRootDirectory ( dir : string ) {
89
106
Debug . assert ( ! resolvedModuleNames . size && ! resolvedTypeReferenceDirectives . size && ! directoryWatchesOfFailedLookups . size ) ;
90
107
rootDir = removeTrailingDirectorySeparator ( getNormalizedAbsolutePath ( dir , getCurrentDirectory ( ) ) ) ;
@@ -100,6 +117,7 @@ namespace ts {
100
117
101
118
function clear ( ) {
102
119
clearMap ( directoryWatchesOfFailedLookups , closeFileWatcherOf ) ;
120
+ customFailedLookupPaths . clear ( ) ;
103
121
closeTypeRootsWatch ( ) ;
104
122
resolvedModuleNames . clear ( ) ;
105
123
resolvedTypeReferenceDirectives . clear ( ) ;
@@ -160,14 +178,13 @@ namespace ts {
160
178
return primaryResult ;
161
179
}
162
180
163
- function resolveNamesWithLocalCache < T extends NameResolutionWithFailedLookupLocations , R > (
181
+ function resolveNamesWithLocalCache < T extends NameResolutionWithFailedLookupLocations , R extends ResolutionWithResolvedFileName > (
164
182
names : string [ ] ,
165
183
containingFile : string ,
166
184
cache : Map < Map < T > > ,
167
185
perDirectoryCache : Map < Map < T > > ,
168
186
loader : ( name : string , containingFile : string , options : CompilerOptions , host : ModuleResolutionHost ) => T ,
169
- getResult : ( s : T ) => R ,
170
- getResultFileName : ( result : R ) => string | undefined ,
187
+ getResolutionFromNameResolutionWithFailedLookupLocations : ( s : T ) => R ,
171
188
logChanges : boolean ) : R [ ] {
172
189
173
190
const path = resolutionHost . toPath ( containingFile ) ;
@@ -208,7 +225,7 @@ namespace ts {
208
225
}
209
226
Debug . assert ( resolution !== undefined && ! resolution . isInvalidated ) ;
210
227
seenNamesInFile . set ( name , true ) ;
211
- resolvedModules . push ( getResult ( resolution ) ) ;
228
+ resolvedModules . push ( getResolutionFromNameResolutionWithFailedLookupLocations ( resolution ) ) ;
212
229
}
213
230
214
231
// Stop watching and remove the unused name
@@ -228,23 +245,23 @@ namespace ts {
228
245
if ( ! oldResolution || ! newResolution || oldResolution . isInvalidated ) {
229
246
return false ;
230
247
}
231
- const oldResult = getResult ( oldResolution ) ;
232
- const newResult = getResult ( newResolution ) ;
248
+ const oldResult = getResolutionFromNameResolutionWithFailedLookupLocations ( oldResolution ) ;
249
+ const newResult = getResolutionFromNameResolutionWithFailedLookupLocations ( newResolution ) ;
233
250
if ( oldResult === newResult ) {
234
251
return true ;
235
252
}
236
253
if ( ! oldResult || ! newResult ) {
237
254
return false ;
238
255
}
239
- return getResultFileName ( oldResult ) === getResultFileName ( newResult ) ;
256
+ return oldResult . resolvedFileName === newResult . resolvedFileName ;
240
257
}
241
258
}
242
259
243
260
function resolveTypeReferenceDirectives ( typeDirectiveNames : string [ ] , containingFile : string ) : ResolvedTypeReferenceDirective [ ] {
244
261
return resolveNamesWithLocalCache (
245
262
typeDirectiveNames , containingFile ,
246
263
resolvedTypeReferenceDirectives , perDirectoryResolvedTypeReferenceDirectives ,
247
- resolveTypeReferenceDirective , m => m . resolvedTypeReferenceDirective , r => r . resolvedFileName ,
264
+ resolveTypeReferenceDirective , getResolvedTypeReferenceDirective ,
248
265
/*logChanges*/ false
249
266
) ;
250
267
}
@@ -253,7 +270,7 @@ namespace ts {
253
270
return resolveNamesWithLocalCache (
254
271
moduleNames , containingFile ,
255
272
resolvedModuleNames , perDirectoryResolvedModuleNames ,
256
- resolveModuleName , m => m . resolvedModule , r => r . resolvedFileName ,
273
+ resolveModuleName , getResolvedModule ,
257
274
logChanges
258
275
) ;
259
276
}
@@ -300,13 +317,24 @@ namespace ts {
300
317
return { dir, dirPath } ;
301
318
}
302
319
320
+ function isPathWithDefaultFailedLookupExtension ( path : Path ) {
321
+ return fileExtensionIsOneOf ( path , failedLookupDefaultExtensions ) ;
322
+ }
323
+
303
324
function watchFailedLookupLocationOfResolution < T extends NameResolutionWithFailedLookupLocations > (
304
325
resolution : T , startIndex ?: number
305
326
) {
306
327
if ( resolution && resolution . failedLookupLocations ) {
307
328
for ( let i = startIndex || 0 ; i < resolution . failedLookupLocations . length ; i ++ ) {
308
329
const failedLookupLocation = resolution . failedLookupLocations [ i ] ;
309
- const { dir, dirPath } = getDirectoryToWatchFailedLookupLocation ( failedLookupLocation , resolutionHost . toPath ( failedLookupLocation ) ) ;
330
+ const failedLookupLocationPath = resolutionHost . toPath ( failedLookupLocation ) ;
331
+ // If the failed lookup location path is not one of the supported extensions,
332
+ // store it in the custom path
333
+ if ( ! isPathWithDefaultFailedLookupExtension ( failedLookupLocationPath ) ) {
334
+ const refCount = customFailedLookupPaths . get ( failedLookupLocationPath ) || 0 ;
335
+ customFailedLookupPaths . set ( failedLookupLocationPath , refCount + 1 ) ;
336
+ }
337
+ const { dir, dirPath } = getDirectoryToWatchFailedLookupLocation ( failedLookupLocation , failedLookupLocationPath ) ;
310
338
const dirWatcher = directoryWatchesOfFailedLookups . get ( dirPath ) ;
311
339
if ( dirWatcher ) {
312
340
dirWatcher . refCount ++ ;
@@ -330,7 +358,17 @@ namespace ts {
330
358
if ( resolution && resolution . failedLookupLocations ) {
331
359
for ( let i = startIndex ; i < resolution . failedLookupLocations . length ; i ++ ) {
332
360
const failedLookupLocation = resolution . failedLookupLocations [ i ] ;
333
- const { dirPath } = getDirectoryToWatchFailedLookupLocation ( failedLookupLocation , resolutionHost . toPath ( failedLookupLocation ) ) ;
361
+ const failedLookupLocationPath = resolutionHost . toPath ( failedLookupLocation ) ;
362
+ const refCount = customFailedLookupPaths . get ( failedLookupLocationPath ) ;
363
+ if ( refCount ) {
364
+ if ( refCount === 1 ) {
365
+ customFailedLookupPaths . delete ( failedLookupLocationPath ) ;
366
+ }
367
+ else {
368
+ customFailedLookupPaths . set ( failedLookupLocationPath , refCount - 1 ) ;
369
+ }
370
+ }
371
+ const { dirPath } = getDirectoryToWatchFailedLookupLocation ( failedLookupLocation , failedLookupLocationPath ) ;
334
372
const dirWatcher = directoryWatchesOfFailedLookups . get ( dirPath ) ;
335
373
// Do not close the watcher yet since it might be needed by other failed lookup locations.
336
374
dirWatcher . refCount -- ;
@@ -349,72 +387,106 @@ namespace ts {
349
387
// If the files are added to project root or node_modules directory, always run through the invalidation process
350
388
// Otherwise run through invalidation only if adding to the immediate directory
351
389
if ( dirPath === rootPath || isNodeModulesDirectory ( dirPath ) || getDirectoryPath ( fileOrFolderPath ) === dirPath ) {
352
- const isChangedFailedLookupLocation : ( location : string ) => boolean = dirPath === fileOrFolderPath ?
353
- // If the file watched directory is created/deleted invalidate any resolution has failed lookup in this directory
354
- location => isInDirectoryPath ( dirPath , resolutionHost . toPath ( location ) ) :
355
- // Otherwise only the resolutions referencing the file or folder added
356
- location => resolutionHost . toPath ( location ) === fileOrFolderPath ;
357
- if ( invalidateResolutionOfFailedLookupLocation ( isChangedFailedLookupLocation ) ) {
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 ) ) {
358
407
resolutionHost . onInvalidatedResolution ( ) ;
359
408
}
360
409
}
361
410
} , WatchDirectoryFlags . Recursive ) ;
362
411
}
363
412
364
- function invalidateResolutionCacheOfDeletedFile < T extends NameResolutionWithFailedLookupLocations , R > (
365
- deletedFilePath : Path ,
413
+ function invalidateResolutionCache < T extends NameResolutionWithFailedLookupLocations > (
366
414
cache : Map < Map < T > > ,
367
- getResult : ( s : T ) => R ,
368
- getResultFileName : ( result : R ) => string | undefined ) {
369
- cache . forEach ( ( value , path ) => {
370
- if ( path === deletedFilePath ) {
371
- cache . delete ( path ) ;
372
- if ( value ) {
373
- value . forEach ( stopWatchFailedLookupLocationOfResolution ) ;
415
+ ignoreFile : ( resolutions : Map < T > , containingFilePath : Path ) => boolean ,
416
+ isInvalidatedResolution : ( resolution : T ) => boolean
417
+ ) {
418
+ const seen = createMap < Map < true > > ( ) ;
419
+ 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 ) ;
374
426
}
375
- }
376
- else if ( value ) {
377
- value . forEach ( resolution => {
378
- if ( resolution && ! resolution . isInvalidated ) {
379
- const result = getResult ( resolution ) ;
380
- if ( result ) {
381
- if ( resolutionHost . toPath ( getResultFileName ( result ) ) === deletedFilePath ) {
382
- resolution . isInvalidated = true ;
383
- ( filesWithInvalidatedResolutions || ( filesWithInvalidatedResolutions = createMap < true > ( ) ) ) . set ( path , true ) ;
384
- }
385
- }
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 ) ;
386
436
}
387
437
} ) ;
388
438
}
389
439
} ) ;
390
440
}
391
441
442
+ function invalidateResolutionCacheOfDeletedFile < T extends NameResolutionWithFailedLookupLocations , R extends ResolutionWithResolvedFileName > (
443
+ deletedFilePath : Path ,
444
+ cache : Map < Map < T > > ,
445
+ getResolutionFromNameResolutionWithFailedLookupLocations : ( s : T ) => R ,
446
+ ) {
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
+ }
454
+
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
+ } ,
460
+ // 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 ;
464
+ }
465
+ ) ;
466
+ }
467
+
392
468
function invalidateResolutionOfFile ( filePath : Path ) {
393
- invalidateResolutionCacheOfDeletedFile ( filePath , resolvedModuleNames , m => m . resolvedModule , r => r . resolvedFileName ) ;
394
- invalidateResolutionCacheOfDeletedFile ( filePath , resolvedTypeReferenceDirectives , m => m . resolvedTypeReferenceDirective , r => r . resolvedFileName ) ;
469
+ invalidateResolutionCacheOfDeletedFile ( filePath , resolvedModuleNames , getResolvedModule ) ;
470
+ invalidateResolutionCacheOfDeletedFile ( filePath , resolvedTypeReferenceDirectives , getResolvedTypeReferenceDirective ) ;
395
471
}
396
472
397
473
function invalidateResolutionCacheOfFailedLookupLocation < T extends NameResolutionWithFailedLookupLocations > (
398
474
cache : Map < Map < T > > ,
399
- isChangedFailedLookupLocation : ( location : string ) => boolean
475
+ hasChangedFailedLookupLocation : ( resolution : T ) => boolean
400
476
) {
401
- cache . forEach ( ( value , containingFile ) => {
402
- if ( value ) {
403
- value . forEach ( resolution => {
404
- if ( resolution && ! resolution . isInvalidated && some ( resolution . failedLookupLocations , isChangedFailedLookupLocation ) ) {
405
- // Mark the file as needing re-evaluation of module resolution instead of using it blindly.
406
- resolution . isInvalidated = true ;
407
- ( filesWithInvalidatedResolutions || ( filesWithInvalidatedResolutions = createMap < true > ( ) ) ) . set ( containingFile , true ) ;
408
- }
409
- } ) ;
410
- }
411
- } ) ;
477
+ invalidateResolutionCache (
478
+ cache ,
479
+ // Do not ignore any file
480
+ returnFalse ,
481
+ // Resolution is invalidated if the resulting file name is same as the deleted file path
482
+ hasChangedFailedLookupLocation
483
+ ) ;
412
484
}
413
485
414
- function invalidateResolutionOfFailedLookupLocation ( isChangedFailedLookupLocation : ( location : string ) => boolean ) {
486
+ function invalidateResolutionOfFailedLookupLocation ( hasChangedFailedLookupLocation : ( resolution : NameResolutionWithFailedLookupLocations ) => boolean ) {
415
487
const invalidatedFilesCount = filesWithInvalidatedResolutions && filesWithInvalidatedResolutions . size ;
416
- invalidateResolutionCacheOfFailedLookupLocation ( resolvedModuleNames , isChangedFailedLookupLocation ) ;
417
- invalidateResolutionCacheOfFailedLookupLocation ( resolvedTypeReferenceDirectives , isChangedFailedLookupLocation ) ;
488
+ invalidateResolutionCacheOfFailedLookupLocation ( resolvedModuleNames , hasChangedFailedLookupLocation ) ;
489
+ invalidateResolutionCacheOfFailedLookupLocation ( resolvedTypeReferenceDirectives , hasChangedFailedLookupLocation ) ;
418
490
return filesWithInvalidatedResolutions && filesWithInvalidatedResolutions . size !== invalidatedFilesCount ;
419
491
}
420
492
0 commit comments