@@ -9,11 +9,13 @@ namespace ts {
9
9
getResolvedModuleWithFailedLookupLocationsFromCache ( moduleName : string , containingFile : string ) : CachedResolvedModuleWithFailedLookupLocations | undefined ;
10
10
resolveTypeReferenceDirectives ( typeDirectiveNames : string [ ] , containingFile : string , redirectedReference ?: ResolvedProjectReference ) : ( ResolvedTypeReferenceDirective | undefined ) [ ] ;
11
11
12
+ invalidateResolutionsOfFailedLookupLocations ( ) : boolean ;
12
13
invalidateResolutionOfFile ( filePath : Path ) : void ;
13
14
removeResolutionsOfFile ( filePath : Path ) : void ;
14
15
removeResolutionsFromProjectReferenceRedirects ( filePath : Path ) : void ;
15
16
setFilesWithInvalidatedNonRelativeUnresolvedImports ( filesWithUnresolvedImports : Map < readonly string [ ] > ) : void ;
16
17
createHasInvalidatedResolution ( forceAllFilesAsInvalidated ?: boolean ) : HasInvalidatedResolution ;
18
+ hasChangedAutomaticTypeDirectiveNames ( ) : boolean ;
17
19
18
20
startCachingPerDirectoryResolution ( ) : void ;
19
21
finishCachingPerDirectoryResolution ( ) : void ;
@@ -50,6 +52,7 @@ namespace ts {
50
52
onInvalidatedResolution ( ) : void ;
51
53
watchTypeRootsDirectory ( directory : string , cb : DirectoryWatcherCallback , flags : WatchDirectoryFlags ) : FileWatcher ;
52
54
onChangedAutomaticTypeDirectiveNames ( ) : void ;
55
+ scheduleInvalidateResolutionsOfFailedLookupLocations ( ) : void ;
53
56
getCachedDirectoryStructureHost ( ) : CachedDirectoryStructureHost | undefined ;
54
57
projectName ?: string ;
55
58
getGlobalCache ?( ) : string | undefined ;
@@ -147,6 +150,11 @@ namespace ts {
147
150
const resolutionsWithFailedLookups : ResolutionWithFailedLookupLocations [ ] = [ ] ;
148
151
const resolvedFileToResolution = createMultiMap < ResolutionWithFailedLookupLocations > ( ) ;
149
152
153
+ let hasChangedAutomaticTypeDirectiveNames = false ;
154
+ const failedLookupChecks : Path [ ] = [ ] ;
155
+ const startsWithPathChecks : Path [ ] = [ ] ;
156
+ const isInDirectoryChecks : Path [ ] = [ ] ;
157
+
150
158
const getCurrentDirectory = memoize ( ( ) => resolutionHost . getCurrentDirectory ! ( ) ) ; // TODO: GH#18217
151
159
const cachedDirectoryStructureHost = resolutionHost . getCachedDirectoryStructureHost ( ) ;
152
160
@@ -195,7 +203,9 @@ namespace ts {
195
203
resolveTypeReferenceDirectives,
196
204
removeResolutionsFromProjectReferenceRedirects,
197
205
removeResolutionsOfFile,
206
+ hasChangedAutomaticTypeDirectiveNames : ( ) => hasChangedAutomaticTypeDirectiveNames ,
198
207
invalidateResolutionOfFile,
208
+ invalidateResolutionsOfFailedLookupLocations,
199
209
setFilesWithInvalidatedNonRelativeUnresolvedImports,
200
210
createHasInvalidatedResolution,
201
211
updateTypeRootsWatch,
@@ -227,9 +237,13 @@ namespace ts {
227
237
resolvedTypeReferenceDirectives . clear ( ) ;
228
238
resolvedFileToResolution . clear ( ) ;
229
239
resolutionsWithFailedLookups . length = 0 ;
240
+ failedLookupChecks . length = 0 ;
241
+ startsWithPathChecks . length = 0 ;
242
+ isInDirectoryChecks . length = 0 ;
230
243
// perDirectoryResolvedModuleNames and perDirectoryResolvedTypeReferenceDirectives could be non empty if there was exception during program update
231
244
// (between startCachingPerDirectoryResolution and finishCachingPerDirectoryResolution)
232
245
clearPerDirectoryResolutions ( ) ;
246
+ hasChangedAutomaticTypeDirectiveNames = false ;
233
247
}
234
248
235
249
function startRecordingFilesWithChangedResolutions ( ) {
@@ -253,6 +267,8 @@ namespace ts {
253
267
}
254
268
255
269
function createHasInvalidatedResolution ( forceAllFilesAsInvalidated ?: boolean ) : HasInvalidatedResolution {
270
+ // Ensure pending resolutions are applied
271
+ invalidateResolutionsOfFailedLookupLocations ( ) ;
256
272
if ( forceAllFilesAsInvalidated ) {
257
273
// Any file asked would have invalidated resolution
258
274
filesWithInvalidatedResolutions = undefined ;
@@ -281,6 +297,7 @@ namespace ts {
281
297
watcher . watcher . close ( ) ;
282
298
}
283
299
} ) ;
300
+ hasChangedAutomaticTypeDirectiveNames = false ;
284
301
}
285
302
286
303
function resolveModuleName ( moduleName : string , containingFile : string , compilerOptions : CompilerOptions , host : ModuleResolutionHost , redirectedReference ?: ResolvedProjectReference ) : CachedResolvedModuleWithFailedLookupLocations {
@@ -662,9 +679,7 @@ namespace ts {
662
679
cachedDirectoryStructureHost . addOrDeleteFileOrDirectory ( fileOrDirectory , fileOrDirectoryPath ) ;
663
680
}
664
681
665
- if ( invalidateResolutionOfFailedLookupLocation ( fileOrDirectoryPath , dirPath === fileOrDirectoryPath ) ) {
666
- resolutionHost . onInvalidatedResolution ( ) ;
667
- }
682
+ scheduleInvalidateResolutionOfFailedLookupLocation ( fileOrDirectoryPath , dirPath === fileOrDirectoryPath ) ;
668
683
} , nonRecursive ? WatchDirectoryFlags . None : WatchDirectoryFlags . Recursive ) ;
669
684
}
670
685
@@ -700,36 +715,42 @@ namespace ts {
700
715
removeResolutionsOfFileFromCache ( resolvedTypeReferenceDirectives , filePath , getResolvedTypeReferenceDirective ) ;
701
716
}
702
717
703
- function invalidateResolution ( resolution : ResolutionWithFailedLookupLocations ) {
704
- resolution . isInvalidated = true ;
705
- let changedInAutoTypeReferenced = false ;
706
- for ( const containingFilePath of Debug . assertDefined ( resolution . files ) ) {
707
- ( filesWithInvalidatedResolutions || ( filesWithInvalidatedResolutions = createMap < true > ( ) ) ) . set ( containingFilePath , true ) ;
708
- // When its a file with inferred types resolution, invalidate type reference directive resolution
709
- changedInAutoTypeReferenced = changedInAutoTypeReferenced || containingFilePath . endsWith ( inferredTypesContainingFile ) ;
710
- }
711
- if ( changedInAutoTypeReferenced ) {
712
- resolutionHost . onChangedAutomaticTypeDirectiveNames ( ) ;
718
+ function invalidateResolutions ( resolutions : ResolutionWithFailedLookupLocations [ ] | undefined , canInvalidate : ( resolution : ResolutionWithFailedLookupLocations ) => boolean ) {
719
+ if ( ! resolutions ) return false ;
720
+ let invalidated = false ;
721
+ for ( const resolution of resolutions ) {
722
+ if ( resolution . isInvalidated || ! canInvalidate ( resolution ) ) continue ;
723
+ resolution . isInvalidated = invalidated = true ;
724
+ for ( const containingFilePath of Debug . assertDefined ( resolution . files ) ) {
725
+ ( filesWithInvalidatedResolutions || ( filesWithInvalidatedResolutions = createMap < true > ( ) ) ) . set ( containingFilePath , true ) ;
726
+ // When its a file with inferred types resolution, invalidate type reference directive resolution
727
+ hasChangedAutomaticTypeDirectiveNames = hasChangedAutomaticTypeDirectiveNames || containingFilePath . endsWith ( inferredTypesContainingFile ) ;
728
+ }
713
729
}
730
+ return invalidated ;
714
731
}
715
732
716
733
function invalidateResolutionOfFile ( filePath : Path ) {
717
734
removeResolutionsOfFile ( filePath ) ;
718
735
// Resolution is invalidated if the resulting file name is same as the deleted file path
719
- forEach ( resolvedFileToResolution . get ( filePath ) , invalidateResolution ) ;
736
+ const prevHasChangedAutomaticTypeDirectiveNames = hasChangedAutomaticTypeDirectiveNames ;
737
+ if ( invalidateResolutions ( resolvedFileToResolution . get ( filePath ) , returnTrue ) &&
738
+ hasChangedAutomaticTypeDirectiveNames &&
739
+ ! prevHasChangedAutomaticTypeDirectiveNames ) {
740
+ resolutionHost . onChangedAutomaticTypeDirectiveNames ( ) ;
741
+ }
720
742
}
721
743
722
744
function setFilesWithInvalidatedNonRelativeUnresolvedImports ( filesMap : ReadonlyMap < readonly string [ ] > ) {
723
745
Debug . assert ( filesWithInvalidatedNonRelativeUnresolvedImports === filesMap || filesWithInvalidatedNonRelativeUnresolvedImports === undefined ) ;
724
746
filesWithInvalidatedNonRelativeUnresolvedImports = filesMap ;
725
747
}
726
748
727
- function invalidateResolutionOfFailedLookupLocation ( fileOrDirectoryPath : Path , isCreatingWatchedDirectory : boolean ) {
728
- let isChangedFailedLookupLocation : ( location : string ) => boolean ;
749
+ function scheduleInvalidateResolutionOfFailedLookupLocation ( fileOrDirectoryPath : Path , isCreatingWatchedDirectory : boolean ) {
729
750
if ( isCreatingWatchedDirectory ) {
730
751
// Watching directory is created
731
752
// Invalidate any resolution has failed lookup in this directory
732
- isChangedFailedLookupLocation = location => isInDirectoryPath ( fileOrDirectoryPath , resolutionHost . toPath ( location ) ) ;
753
+ isInDirectoryChecks . push ( fileOrDirectoryPath ) ;
733
754
}
734
755
else {
735
756
// If something to do with folder/file starting with "." in node_modules folder, skip it
@@ -748,10 +769,8 @@ namespace ts {
748
769
if ( isNodeModulesAtTypesDirectory ( fileOrDirectoryPath ) || isNodeModulesDirectory ( fileOrDirectoryPath ) ||
749
770
isNodeModulesAtTypesDirectory ( dirOfFileOrDirectory ) || isNodeModulesDirectory ( dirOfFileOrDirectory ) ) {
750
771
// Invalidate any resolution from this directory
751
- isChangedFailedLookupLocation = location => {
752
- const locationPath = resolutionHost . toPath ( location ) ;
753
- return locationPath === fileOrDirectoryPath || startsWith ( resolutionHost . toPath ( location ) , fileOrDirectoryPath ) ;
754
- } ;
772
+ failedLookupChecks . push ( fileOrDirectoryPath ) ;
773
+ startsWithPathChecks . push ( fileOrDirectoryPath ) ;
755
774
}
756
775
else {
757
776
if ( ! isPathWithDefaultFailedLookupExtension ( fileOrDirectoryPath ) && ! customFailedLookupPaths . has ( fileOrDirectoryPath ) ) {
@@ -762,20 +781,33 @@ namespace ts {
762
781
return false ;
763
782
}
764
783
// Resolution need to be invalidated if failed lookup location is same as the file or directory getting created
765
- isChangedFailedLookupLocation = location => resolutionHost . toPath ( location ) === fileOrDirectoryPath ;
784
+ failedLookupChecks . push ( fileOrDirectoryPath ) ;
766
785
}
767
786
}
768
- let invalidated = false ;
769
- // Resolution is invalidated if the resulting file name is same as the deleted file path
770
- for ( const resolution of resolutionsWithFailedLookups ) {
771
- if ( resolution . failedLookupLocations . some ( isChangedFailedLookupLocation ) ) {
772
- invalidateResolution ( resolution ) ;
773
- invalidated = true ;
774
- }
787
+ resolutionHost . scheduleInvalidateResolutionsOfFailedLookupLocations ( ) ;
788
+ }
789
+
790
+ function invalidateResolutionsOfFailedLookupLocations ( ) {
791
+ if ( ! failedLookupChecks . length && ! startsWithPathChecks . length && ! isInDirectoryChecks . length ) {
792
+ return false ;
775
793
}
794
+
795
+ const invalidated = invalidateResolutions ( resolutionsWithFailedLookups , canInvalidateFailedLookupResolution ) ;
796
+ failedLookupChecks . length = 0 ;
797
+ startsWithPathChecks . length = 0 ;
798
+ isInDirectoryChecks . length = 0 ;
776
799
return invalidated ;
777
800
}
778
801
802
+ function canInvalidateFailedLookupResolution ( resolution : ResolutionWithFailedLookupLocations ) {
803
+ return resolution . failedLookupLocations . some ( location => {
804
+ const locationPath = resolutionHost . toPath ( location ) ;
805
+ return contains ( failedLookupChecks , locationPath ) ||
806
+ startsWithPathChecks . some ( fileOrDirectoryPath => startsWith ( locationPath , fileOrDirectoryPath ) ) ||
807
+ isInDirectoryChecks . some ( fileOrDirectoryPath => isInDirectoryPath ( fileOrDirectoryPath , locationPath ) ) ;
808
+ } ) ;
809
+ }
810
+
779
811
function closeTypeRootsWatch ( ) {
780
812
clearMap ( typeRootsWatches , closeFileWatcher ) ;
781
813
}
@@ -800,13 +832,14 @@ namespace ts {
800
832
// For now just recompile
801
833
// We could potentially store more data here about whether it was/would be really be used or not
802
834
// and with that determine to trigger compilation but for now this is enough
835
+ hasChangedAutomaticTypeDirectiveNames = true ;
803
836
resolutionHost . onChangedAutomaticTypeDirectiveNames ( ) ;
804
837
805
838
// Since directory watchers invoked are flaky, the failed lookup location events might not be triggered
806
839
// So handle to failed lookup locations here as well to ensure we are invalidating resolutions
807
840
const dirPath = getDirectoryToWatchFailedLookupLocationFromTypeRoot ( typeRoot , typeRootPath ) ;
808
- if ( dirPath && invalidateResolutionOfFailedLookupLocation ( fileOrDirectoryPath , dirPath === fileOrDirectoryPath ) ) {
809
- resolutionHost . onInvalidatedResolution ( ) ;
841
+ if ( dirPath ) {
842
+ scheduleInvalidateResolutionOfFailedLookupLocation ( fileOrDirectoryPath , dirPath === fileOrDirectoryPath ) ;
810
843
}
811
844
} , WatchDirectoryFlags . Recursive ) ;
812
845
}
0 commit comments