@@ -153,9 +153,9 @@ namespace ts {
153153 const resolvedFileToResolution = createMultiMap < ResolutionWithFailedLookupLocations > ( ) ;
154154
155155 let hasChangedAutomaticTypeDirectiveNames = false ;
156- const failedLookupChecks : Path [ ] = [ ] ;
157- const startsWithPathChecks : Path [ ] = [ ] ;
158- const isInDirectoryChecks : Path [ ] = [ ] ;
156+ let failedLookupChecks : Path [ ] | undefined ;
157+ let startsWithPathChecks : Set < Path > | undefined ;
158+ let isInDirectoryChecks : Path [ ] | undefined ;
159159
160160 const getCurrentDirectory = memoize ( ( ) => resolutionHost . getCurrentDirectory ! ( ) ) ; // TODO: GH#18217
161161 const cachedDirectoryStructureHost = resolutionHost . getCachedDirectoryStructureHost ( ) ;
@@ -248,9 +248,9 @@ namespace ts {
248248 resolvedTypeReferenceDirectives . clear ( ) ;
249249 resolvedFileToResolution . clear ( ) ;
250250 resolutionsWithFailedLookups . length = 0 ;
251- failedLookupChecks . length = 0 ;
252- startsWithPathChecks . length = 0 ;
253- isInDirectoryChecks . length = 0 ;
251+ failedLookupChecks = undefined ;
252+ startsWithPathChecks = undefined ;
253+ isInDirectoryChecks = undefined ;
254254 // perDirectoryResolvedModuleNames and perDirectoryResolvedTypeReferenceDirectives could be non empty if there was exception during program update
255255 // (between startCachingPerDirectoryResolution and finishCachingPerDirectoryResolution)
256256 clearPerDirectoryResolutions ( ) ;
@@ -766,7 +766,7 @@ namespace ts {
766766 if ( isCreatingWatchedDirectory ) {
767767 // Watching directory is created
768768 // Invalidate any resolution has failed lookup in this directory
769- isInDirectoryChecks . push ( fileOrDirectoryPath ) ;
769+ ( isInDirectoryChecks ||= [ ] ) . push ( fileOrDirectoryPath ) ;
770770 }
771771 else {
772772 // If something to do with folder/file starting with "." in node_modules folder, skip it
@@ -785,8 +785,8 @@ namespace ts {
785785 if ( isNodeModulesAtTypesDirectory ( fileOrDirectoryPath ) || isNodeModulesDirectory ( fileOrDirectoryPath ) ||
786786 isNodeModulesAtTypesDirectory ( dirOfFileOrDirectory ) || isNodeModulesDirectory ( dirOfFileOrDirectory ) ) {
787787 // Invalidate any resolution from this directory
788- failedLookupChecks . push ( fileOrDirectoryPath ) ;
789- startsWithPathChecks . push ( fileOrDirectoryPath ) ;
788+ ( failedLookupChecks ||= [ ] ) . push ( fileOrDirectoryPath ) ;
789+ ( startsWithPathChecks ||= new Set ( ) ) . add ( fileOrDirectoryPath ) ;
790790 }
791791 else {
792792 if ( ! isPathWithDefaultFailedLookupExtension ( fileOrDirectoryPath ) && ! customFailedLookupPaths . has ( fileOrDirectoryPath ) ) {
@@ -797,30 +797,36 @@ namespace ts {
797797 return false ;
798798 }
799799 // Resolution need to be invalidated if failed lookup location is same as the file or directory getting created
800- failedLookupChecks . push ( fileOrDirectoryPath ) ;
800+ ( failedLookupChecks ||= [ ] ) . push ( fileOrDirectoryPath ) ;
801+
802+ // If the invalidated file is from a node_modules package, invalidate everything else
803+ // in the package since we might not get notifications for other files in the package.
804+ // This hardens our logic against unreliable file watchers.
805+ const packagePath = parseNodeModuleFromPath ( fileOrDirectoryPath ) ;
806+ if ( packagePath ) ( startsWithPathChecks ||= new Set ( ) ) . add ( packagePath as Path ) ;
801807 }
802808 }
803809 resolutionHost . scheduleInvalidateResolutionsOfFailedLookupLocations ( ) ;
804810 }
805811
806812 function invalidateResolutionsOfFailedLookupLocations ( ) {
807- if ( ! failedLookupChecks . length && ! startsWithPathChecks . length && ! isInDirectoryChecks . length ) {
813+ if ( ! failedLookupChecks && ! startsWithPathChecks && ! isInDirectoryChecks ) {
808814 return false ;
809815 }
810816
811817 const invalidated = invalidateResolutions ( resolutionsWithFailedLookups , canInvalidateFailedLookupResolution ) ;
812- failedLookupChecks . length = 0 ;
813- startsWithPathChecks . length = 0 ;
814- isInDirectoryChecks . length = 0 ;
818+ failedLookupChecks = undefined ;
819+ startsWithPathChecks = undefined ;
820+ isInDirectoryChecks = undefined ;
815821 return invalidated ;
816822 }
817823
818824 function canInvalidateFailedLookupResolution ( resolution : ResolutionWithFailedLookupLocations ) {
819825 return resolution . failedLookupLocations . some ( location => {
820826 const locationPath = resolutionHost . toPath ( location ) ;
821827 return contains ( failedLookupChecks , locationPath ) ||
822- startsWithPathChecks . some ( fileOrDirectoryPath => startsWith ( locationPath , fileOrDirectoryPath ) ) ||
823- isInDirectoryChecks . some ( fileOrDirectoryPath => isInDirectoryPath ( fileOrDirectoryPath , locationPath ) ) ;
828+ firstDefinedIterator ( startsWithPathChecks ?. keys ( ) || emptyIterator , fileOrDirectoryPath => startsWith ( locationPath , fileOrDirectoryPath ) ? true : undefined ) ||
829+ isInDirectoryChecks ? .some ( fileOrDirectoryPath => isInDirectoryPath ( fileOrDirectoryPath , locationPath ) ) ;
824830 } ) ;
825831 }
826832
0 commit comments