@@ -30,6 +30,10 @@ namespace ts {
3030 isInvalidated ?: boolean ;
3131 }
3232
33+ interface ResolutionWithResolvedFileName {
34+ resolvedFileName : string | undefined ;
35+ }
36+
3337 export interface ResolutionCacheHost extends ModuleResolutionHost {
3438 toPath ( fileName : string ) : Path ;
3539 getCompilationSettings ( ) : CompilerOptions ;
@@ -59,10 +63,15 @@ namespace ts {
5963 // The values are Map of resolutions with key being name lookedup.
6064 const resolvedModuleNames = createMap < Map < ResolvedModuleWithFailedLookupLocations > > ( ) ;
6165 const perDirectoryResolvedModuleNames = createMap < Map < ResolvedModuleWithFailedLookupLocations > > ( ) ;
66+
6267 const resolvedTypeReferenceDirectives = createMap < Map < ResolvedTypeReferenceDirectiveWithFailedLookupLocations > > ( ) ;
6368 const perDirectoryResolvedTypeReferenceDirectives = createMap < Map < ResolvedTypeReferenceDirectiveWithFailedLookupLocations > > ( ) ;
69+
6470 const getCurrentDirectory = memoize ( ( ) => resolutionHost . getCurrentDirectory ( ) ) ;
6571
72+ const failedLookupDefaultExtensions = [ Extension . Ts , Extension . Tsx , Extension . Js , Extension . Jsx , Extension . Json ] ;
73+ const customFailedLookupPaths = createMap < number > ( ) ;
74+
6675 const directoryWatchesOfFailedLookups = createMap < DirectoryWatchesOfFailedLookup > ( ) ;
6776 let rootDir : string ;
6877 let rootPath : Path ;
@@ -85,6 +94,14 @@ namespace ts {
8594 clear
8695 } ;
8796
97+ function getResolvedModule ( resolution : ResolvedModuleWithFailedLookupLocations ) {
98+ return resolution . resolvedModule ;
99+ }
100+
101+ function getResolvedTypeReferenceDirective ( resolution : ResolvedTypeReferenceDirectiveWithFailedLookupLocations ) {
102+ return resolution . resolvedTypeReferenceDirective ;
103+ }
104+
88105 function setRootDirectory ( dir : string ) {
89106 Debug . assert ( ! resolvedModuleNames . size && ! resolvedTypeReferenceDirectives . size && ! directoryWatchesOfFailedLookups . size ) ;
90107 rootDir = removeTrailingDirectorySeparator ( getNormalizedAbsolutePath ( dir , getCurrentDirectory ( ) ) ) ;
@@ -100,6 +117,7 @@ namespace ts {
100117
101118 function clear ( ) {
102119 clearMap ( directoryWatchesOfFailedLookups , closeFileWatcherOf ) ;
120+ customFailedLookupPaths . clear ( ) ;
103121 closeTypeRootsWatch ( ) ;
104122 resolvedModuleNames . clear ( ) ;
105123 resolvedTypeReferenceDirectives . clear ( ) ;
@@ -160,14 +178,13 @@ namespace ts {
160178 return primaryResult ;
161179 }
162180
163- function resolveNamesWithLocalCache < T extends NameResolutionWithFailedLookupLocations , R > (
181+ function resolveNamesWithLocalCache < T extends NameResolutionWithFailedLookupLocations , R extends ResolutionWithResolvedFileName > (
164182 names : string [ ] ,
165183 containingFile : string ,
166184 cache : Map < Map < T > > ,
167185 perDirectoryCache : Map < Map < T > > ,
168186 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 ,
171188 logChanges : boolean ) : R [ ] {
172189
173190 const path = resolutionHost . toPath ( containingFile ) ;
@@ -208,7 +225,7 @@ namespace ts {
208225 }
209226 Debug . assert ( resolution !== undefined && ! resolution . isInvalidated ) ;
210227 seenNamesInFile . set ( name , true ) ;
211- resolvedModules . push ( getResult ( resolution ) ) ;
228+ resolvedModules . push ( getResolutionFromNameResolutionWithFailedLookupLocations ( resolution ) ) ;
212229 }
213230
214231 // Stop watching and remove the unused name
@@ -228,23 +245,23 @@ namespace ts {
228245 if ( ! oldResolution || ! newResolution || oldResolution . isInvalidated ) {
229246 return false ;
230247 }
231- const oldResult = getResult ( oldResolution ) ;
232- const newResult = getResult ( newResolution ) ;
248+ const oldResult = getResolutionFromNameResolutionWithFailedLookupLocations ( oldResolution ) ;
249+ const newResult = getResolutionFromNameResolutionWithFailedLookupLocations ( newResolution ) ;
233250 if ( oldResult === newResult ) {
234251 return true ;
235252 }
236253 if ( ! oldResult || ! newResult ) {
237254 return false ;
238255 }
239- return getResultFileName ( oldResult ) === getResultFileName ( newResult ) ;
256+ return oldResult . resolvedFileName === newResult . resolvedFileName ;
240257 }
241258 }
242259
243260 function resolveTypeReferenceDirectives ( typeDirectiveNames : string [ ] , containingFile : string ) : ResolvedTypeReferenceDirective [ ] {
244261 return resolveNamesWithLocalCache (
245262 typeDirectiveNames , containingFile ,
246263 resolvedTypeReferenceDirectives , perDirectoryResolvedTypeReferenceDirectives ,
247- resolveTypeReferenceDirective , m => m . resolvedTypeReferenceDirective , r => r . resolvedFileName ,
264+ resolveTypeReferenceDirective , getResolvedTypeReferenceDirective ,
248265 /*logChanges*/ false
249266 ) ;
250267 }
@@ -253,7 +270,7 @@ namespace ts {
253270 return resolveNamesWithLocalCache (
254271 moduleNames , containingFile ,
255272 resolvedModuleNames , perDirectoryResolvedModuleNames ,
256- resolveModuleName , m => m . resolvedModule , r => r . resolvedFileName ,
273+ resolveModuleName , getResolvedModule ,
257274 logChanges
258275 ) ;
259276 }
@@ -300,13 +317,24 @@ namespace ts {
300317 return { dir, dirPath } ;
301318 }
302319
320+ function isPathWithDefaultFailedLookupExtension ( path : Path ) {
321+ return fileExtensionIsOneOf ( path , failedLookupDefaultExtensions ) ;
322+ }
323+
303324 function watchFailedLookupLocationOfResolution < T extends NameResolutionWithFailedLookupLocations > (
304325 resolution : T , startIndex ?: number
305326 ) {
306327 if ( resolution && resolution . failedLookupLocations ) {
307328 for ( let i = startIndex || 0 ; i < resolution . failedLookupLocations . length ; i ++ ) {
308329 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 ) ;
310338 const dirWatcher = directoryWatchesOfFailedLookups . get ( dirPath ) ;
311339 if ( dirWatcher ) {
312340 dirWatcher . refCount ++ ;
@@ -330,7 +358,17 @@ namespace ts {
330358 if ( resolution && resolution . failedLookupLocations ) {
331359 for ( let i = startIndex ; i < resolution . failedLookupLocations . length ; i ++ ) {
332360 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 ) ;
334372 const dirWatcher = directoryWatchesOfFailedLookups . get ( dirPath ) ;
335373 // Do not close the watcher yet since it might be needed by other failed lookup locations.
336374 dirWatcher . refCount -- ;
@@ -349,72 +387,106 @@ namespace ts {
349387 // If the files are added to project root or node_modules directory, always run through the invalidation process
350388 // Otherwise run through invalidation only if adding to the immediate directory
351389 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 ) ) {
358407 resolutionHost . onInvalidatedResolution ( ) ;
359408 }
360409 }
361410 } , WatchDirectoryFlags . Recursive ) ;
362411 }
363412
364- function invalidateResolutionCacheOfDeletedFile < T extends NameResolutionWithFailedLookupLocations , R > (
365- deletedFilePath : Path ,
413+ function invalidateResolutionCache < T extends NameResolutionWithFailedLookupLocations > (
366414 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 ) ;
374426 }
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 ) ;
386436 }
387437 } ) ;
388438 }
389439 } ) ;
390440 }
391441
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+
392468 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 ) ;
395471 }
396472
397473 function invalidateResolutionCacheOfFailedLookupLocation < T extends NameResolutionWithFailedLookupLocations > (
398474 cache : Map < Map < T > > ,
399- isChangedFailedLookupLocation : ( location : string ) => boolean
475+ hasChangedFailedLookupLocation : ( resolution : T ) => boolean
400476 ) {
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+ ) ;
412484 }
413485
414- function invalidateResolutionOfFailedLookupLocation ( isChangedFailedLookupLocation : ( location : string ) => boolean ) {
486+ function invalidateResolutionOfFailedLookupLocation ( hasChangedFailedLookupLocation : ( resolution : NameResolutionWithFailedLookupLocations ) => boolean ) {
415487 const invalidatedFilesCount = filesWithInvalidatedResolutions && filesWithInvalidatedResolutions . size ;
416- invalidateResolutionCacheOfFailedLookupLocation ( resolvedModuleNames , isChangedFailedLookupLocation ) ;
417- invalidateResolutionCacheOfFailedLookupLocation ( resolvedTypeReferenceDirectives , isChangedFailedLookupLocation ) ;
488+ invalidateResolutionCacheOfFailedLookupLocation ( resolvedModuleNames , hasChangedFailedLookupLocation ) ;
489+ invalidateResolutionCacheOfFailedLookupLocation ( resolvedTypeReferenceDirectives , hasChangedFailedLookupLocation ) ;
418490 return filesWithInvalidatedResolutions && filesWithInvalidatedResolutions . size !== invalidatedFilesCount ;
419491 }
420492
0 commit comments