@@ -54,6 +54,7 @@ import {
54
54
normalizePath ,
55
55
PackageId ,
56
56
packageIdToString ,
57
+ PackageJsonInfo ,
57
58
parseNodeModuleFromPath ,
58
59
Path ,
59
60
PathPathComponents ,
@@ -213,7 +214,7 @@ export interface FileWatcherOfAffectingLocation {
213
214
watcher : FileWatcher ;
214
215
resolutions : number ;
215
216
files : number ;
216
- paths : Set < string > ;
217
+ symlinks : Set < string > | undefined ;
217
218
}
218
219
219
220
/** @internal */
@@ -723,12 +724,11 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
723
724
}
724
725
} ) ;
725
726
fileWatchesOfAffectingLocations . forEach ( ( watcher , path ) => {
726
- if ( watcher . files === 0 && watcher . resolutions === 0 ) {
727
+ if ( watcher . files === 0 && watcher . resolutions === 0 && ! watcher . symlinks ?. size ) {
727
728
fileWatchesOfAffectingLocations . delete ( path ) ;
728
729
watcher . watcher . close ( ) ;
729
730
}
730
731
} ) ;
731
-
732
732
hasChangedAutomaticTypeDirectiveNames = false ;
733
733
}
734
734
@@ -1057,51 +1057,63 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
1057
1057
return ;
1058
1058
}
1059
1059
let locationToWatch = affectingLocation ;
1060
+ let isSymlink = false ;
1061
+ let symlinkWatcher : FileWatcherOfAffectingLocation | undefined ;
1060
1062
if ( resolutionHost . realpath ) {
1061
1063
locationToWatch = resolutionHost . realpath ( affectingLocation ) ;
1062
1064
if ( affectingLocation !== locationToWatch ) {
1063
- const fileWatcher = fileWatchesOfAffectingLocations . get ( locationToWatch ) ;
1064
- if ( fileWatcher ) {
1065
- if ( forResolution ) fileWatcher . resolutions ++ ;
1066
- else fileWatcher . files ++ ;
1067
- fileWatcher . paths . add ( affectingLocation ) ;
1068
- fileWatchesOfAffectingLocations . set ( affectingLocation , fileWatcher ) ;
1069
- return ;
1070
- }
1065
+ isSymlink = true ;
1066
+ symlinkWatcher = fileWatchesOfAffectingLocations . get ( locationToWatch ) ;
1071
1067
}
1072
1068
}
1073
- const paths = new Set < string > ( ) ;
1074
- paths . add ( locationToWatch ) ;
1075
- let actualWatcher = canWatchAffectingLocation ( resolutionHost . toPath ( locationToWatch ) ) ?
1076
- resolutionHost . watchAffectingFileLocation ( locationToWatch , ( fileName , eventKind ) => {
1077
- cachedDirectoryStructureHost ?. addOrDeleteFile ( fileName , resolutionHost . toPath ( locationToWatch ) , eventKind ) ;
1078
- const packageJsonMap = moduleResolutionCache . getPackageJsonInfoCache ( ) . getInternalMap ( ) ;
1079
- paths . forEach ( path => {
1080
- if ( watcher . resolutions ) ( affectingPathChecks ??= new Set ( ) ) . add ( path ) ;
1081
- if ( watcher . files ) ( affectingPathChecksForFile ??= new Set ( ) ) . add ( path ) ;
1082
- packageJsonMap ?. delete ( resolutionHost . toPath ( path ) ) ;
1083
- } ) ;
1084
- resolutionHost . scheduleInvalidateResolutionsOfFailedLookupLocations ( ) ;
1085
- } ) : noopFileWatcher ;
1086
- const watcher : FileWatcherOfAffectingLocation = {
1087
- watcher : actualWatcher !== noopFileWatcher ? {
1088
- close : ( ) => {
1089
- actualWatcher . close ( ) ;
1090
- // Ensure when watching symlinked package.json, we can close the actual file watcher only once
1091
- actualWatcher = noopFileWatcher ;
1092
- }
1093
- } : actualWatcher ,
1094
- resolutions : forResolution ? 1 : 0 ,
1095
- files : forResolution ? 0 : 1 ,
1096
- paths,
1097
- } ;
1098
- fileWatchesOfAffectingLocations . set ( locationToWatch , watcher ) ;
1099
- if ( affectingLocation !== locationToWatch ) {
1069
+
1070
+ const resolutions = forResolution ? 1 : 0 ;
1071
+ const files = forResolution ? 0 : 1 ;
1072
+ if ( ! isSymlink || ! symlinkWatcher ) {
1073
+ const watcher : FileWatcherOfAffectingLocation = {
1074
+ watcher : canWatchAffectingLocation ( resolutionHost . toPath ( locationToWatch ) ) ?
1075
+ resolutionHost . watchAffectingFileLocation ( locationToWatch , ( fileName , eventKind ) => {
1076
+ cachedDirectoryStructureHost ?. addOrDeleteFile ( fileName , resolutionHost . toPath ( locationToWatch ) , eventKind ) ;
1077
+ invalidateAffectingFileWatcher ( locationToWatch , moduleResolutionCache . getPackageJsonInfoCache ( ) . getInternalMap ( ) ) ;
1078
+ resolutionHost . scheduleInvalidateResolutionsOfFailedLookupLocations ( ) ;
1079
+ } ) : noopFileWatcher ,
1080
+ resolutions : isSymlink ? 0 : resolutions ,
1081
+ files : isSymlink ? 0 : files ,
1082
+ symlinks : undefined ,
1083
+ } ;
1084
+ fileWatchesOfAffectingLocations . set ( locationToWatch , watcher ) ;
1085
+ if ( isSymlink ) symlinkWatcher = watcher ;
1086
+ }
1087
+ if ( isSymlink ) {
1088
+ Debug . assert ( ! ! symlinkWatcher ) ;
1089
+ const watcher : FileWatcherOfAffectingLocation = {
1090
+ watcher : {
1091
+ close : ( ) => {
1092
+ const symlinkWatcher = fileWatchesOfAffectingLocations . get ( locationToWatch ) ;
1093
+ // Close symlink watcher if no ref
1094
+ if ( symlinkWatcher ?. symlinks ?. delete ( affectingLocation ) && ! symlinkWatcher . symlinks . size && ! symlinkWatcher . resolutions && ! symlinkWatcher . files ) {
1095
+ fileWatchesOfAffectingLocations . delete ( locationToWatch ) ;
1096
+ symlinkWatcher . watcher . close ( ) ;
1097
+ }
1098
+ } ,
1099
+ } ,
1100
+ resolutions,
1101
+ files,
1102
+ symlinks : undefined ,
1103
+ } ;
1100
1104
fileWatchesOfAffectingLocations . set ( affectingLocation , watcher ) ;
1101
- paths . add ( affectingLocation ) ;
1105
+ ( symlinkWatcher . symlinks ??= new Set ( ) ) . add ( affectingLocation ) ;
1102
1106
}
1103
1107
}
1104
1108
1109
+ function invalidateAffectingFileWatcher ( path : string , packageJsonMap : Map < Path , PackageJsonInfo | boolean > | undefined ) {
1110
+ const watcher = fileWatchesOfAffectingLocations . get ( path ) ;
1111
+ if ( watcher ?. resolutions ) ( affectingPathChecks ??= new Set ( ) ) . add ( path ) ;
1112
+ if ( watcher ?. files ) ( affectingPathChecksForFile ??= new Set ( ) ) . add ( path ) ;
1113
+ watcher ?. symlinks ?. forEach ( path => invalidateAffectingFileWatcher ( path , packageJsonMap ) ) ;
1114
+ packageJsonMap ?. delete ( resolutionHost . toPath ( path ) ) ;
1115
+ }
1116
+
1105
1117
function watchFailedLookupLocationOfNonRelativeModuleResolutions ( resolutions : ResolutionWithFailedLookupLocations [ ] , name : string ) {
1106
1118
const program = resolutionHost . getCurrentProgram ( ) ;
1107
1119
if ( ! program || ! program . getTypeChecker ( ) . tryFindAmbientModuleWithoutAugmentations ( name ) ) {
0 commit comments