@@ -35,8 +35,7 @@ namespace ts {
35
35
36
36
export type FileWatcherCallback = ( fileName : string , eventKind : FileWatcherEventKind , modifiedTime ?: Date ) => void ;
37
37
export type DirectoryWatcherCallback = ( fileName : string ) => void ;
38
- /*@internal */
39
- export interface WatchedFile {
38
+ interface WatchedFile {
40
39
readonly fileName : string ;
41
40
readonly callback : FileWatcherCallback ;
42
41
mtime : Date ;
@@ -81,8 +80,7 @@ namespace ts {
81
80
/* @internal */
82
81
export let unchangedPollThresholds = createPollingIntervalBasedLevels ( defaultChunkLevels ) ;
83
82
84
- /* @internal */
85
- export function setCustomPollingValues ( system : System ) {
83
+ function setCustomPollingValues ( system : System ) {
86
84
if ( ! system . getEnvironmentVariable ) {
87
85
return ;
88
86
}
@@ -189,31 +187,28 @@ namespace ts {
189
187
}
190
188
}
191
189
192
- /* @internal */
193
- export function createDynamicPriorityPollingWatchFile ( host : {
190
+ interface WatchedFileWithUnchangedPolls extends WatchedFileWithIsClosed {
191
+ unchangedPolls : number ;
192
+ }
193
+ function createDynamicPriorityPollingWatchFile ( host : {
194
194
getModifiedTime : NonNullable < System [ "getModifiedTime" ] > ;
195
195
setTimeout : NonNullable < System [ "setTimeout" ] > ;
196
196
} ) : HostWatchFile {
197
- interface WatchedFile extends ts . WatchedFile {
198
- isClosed ?: boolean ;
199
- unchangedPolls : number ;
200
- }
201
-
202
- interface PollingIntervalQueue extends Array < WatchedFile > {
197
+ interface PollingIntervalQueue extends Array < WatchedFileWithUnchangedPolls > {
203
198
pollingInterval : PollingInterval ;
204
199
pollIndex : number ;
205
200
pollScheduled : boolean ;
206
201
}
207
202
208
- const watchedFiles : WatchedFile [ ] = [ ] ;
209
- const changedFilesInLastPoll : WatchedFile [ ] = [ ] ;
203
+ const watchedFiles : WatchedFileWithUnchangedPolls [ ] = [ ] ;
204
+ const changedFilesInLastPoll : WatchedFileWithUnchangedPolls [ ] = [ ] ;
210
205
const lowPollingIntervalQueue = createPollingIntervalQueue ( PollingInterval . Low ) ;
211
206
const mediumPollingIntervalQueue = createPollingIntervalQueue ( PollingInterval . Medium ) ;
212
207
const highPollingIntervalQueue = createPollingIntervalQueue ( PollingInterval . High ) ;
213
208
return watchFile ;
214
209
215
210
function watchFile ( fileName : string , callback : FileWatcherCallback , defaultPollingInterval : PollingInterval ) : FileWatcher {
216
- const file : WatchedFile = {
211
+ const file : WatchedFileWithUnchangedPolls = {
217
212
fileName,
218
213
callback,
219
214
unchangedPolls : 0 ,
@@ -233,7 +228,7 @@ namespace ts {
233
228
}
234
229
235
230
function createPollingIntervalQueue ( pollingInterval : PollingInterval ) : PollingIntervalQueue {
236
- const queue = [ ] as WatchedFile [ ] as PollingIntervalQueue ;
231
+ const queue = [ ] as WatchedFileWithUnchangedPolls [ ] as PollingIntervalQueue ;
237
232
queue . pollingInterval = pollingInterval ;
238
233
queue . pollIndex = 0 ;
239
234
queue . pollScheduled = false ;
@@ -265,7 +260,7 @@ namespace ts {
265
260
}
266
261
}
267
262
268
- function pollQueue ( queue : ( WatchedFile | undefined ) [ ] , pollingInterval : PollingInterval , pollIndex : number , chunkSize : number ) {
263
+ function pollQueue ( queue : ( WatchedFileWithUnchangedPolls | undefined ) [ ] , pollingInterval : PollingInterval , pollIndex : number , chunkSize : number ) {
269
264
return pollWatchedFileQueue (
270
265
host ,
271
266
queue ,
@@ -274,7 +269,7 @@ namespace ts {
274
269
onWatchFileStat
275
270
) ;
276
271
277
- function onWatchFileStat ( watchedFile : WatchedFile , pollIndex : number , fileChanged : boolean ) {
272
+ function onWatchFileStat ( watchedFile : WatchedFileWithUnchangedPolls , pollIndex : number , fileChanged : boolean ) {
278
273
if ( fileChanged ) {
279
274
watchedFile . unchangedPolls = 0 ;
280
275
// Changed files go to changedFilesInLastPoll queue
@@ -311,12 +306,12 @@ namespace ts {
311
306
}
312
307
}
313
308
314
- function addToPollingIntervalQueue ( file : WatchedFile , pollingInterval : PollingInterval ) {
309
+ function addToPollingIntervalQueue ( file : WatchedFileWithUnchangedPolls , pollingInterval : PollingInterval ) {
315
310
pollingIntervalQueue ( pollingInterval ) . push ( file ) ;
316
311
scheduleNextPollIfNotAlreadyScheduled ( pollingInterval ) ;
317
312
}
318
313
319
- function addChangedFileToLowPollingIntervalQueue ( file : WatchedFile ) {
314
+ function addChangedFileToLowPollingIntervalQueue ( file : WatchedFileWithUnchangedPolls ) {
320
315
changedFilesInLastPoll . push ( file ) ;
321
316
scheduleNextPollIfNotAlreadyScheduled ( PollingInterval . Low ) ;
322
317
}
@@ -423,59 +418,50 @@ namespace ts {
423
418
}
424
419
}
425
420
426
- /* @internal */
427
- export function createSingleFileWatcherPerName (
428
- watchFile : HostWatchFile ,
429
- useCaseSensitiveFileNames : boolean
430
- ) : HostWatchFile {
431
- interface SingleFileWatcher {
432
- watcher : FileWatcher ;
433
- refCount : number ;
434
- }
435
- const cache = new Map < string , SingleFileWatcher > ( ) ;
436
- const callbacksCache = createMultiMap < FileWatcherCallback > ( ) ;
421
+ interface SingleFileWatcher < T extends FileWatcherCallback | FsWatchCallback > {
422
+ watcher : FileWatcher ;
423
+ callbacks : T [ ] ;
424
+ }
425
+ function createSingleWatcherPerName < T extends FileWatcherCallback | FsWatchCallback > (
426
+ cache : Map < SingleFileWatcher < T > > ,
427
+ useCaseSensitiveFileNames : boolean ,
428
+ name : string ,
429
+ callback : T ,
430
+ createWatcher : ( callback : T ) => FileWatcher ,
431
+ ) : FileWatcher {
437
432
const toCanonicalFileName = createGetCanonicalFileName ( useCaseSensitiveFileNames ) ;
433
+ const path = toCanonicalFileName ( name ) ;
434
+ const existing = cache . get ( path ) ;
435
+ if ( existing ) {
436
+ existing . callbacks . push ( callback ) ;
437
+ }
438
+ else {
439
+ cache . set ( path , {
440
+ watcher : createWatcher ( (
441
+ // Cant infer types correctly so lets satisfy checker
442
+ ( param1 : any , param2 : never , param3 : any ) => cache . get ( path ) ?. callbacks . slice ( ) . forEach ( cb => cb ( param1 , param2 , param3 ) )
443
+ ) as T ) ,
444
+ callbacks : [ callback ]
445
+ } ) ;
446
+ }
438
447
439
- return ( fileName , callback , pollingInterval , options ) => {
440
- const path = toCanonicalFileName ( fileName ) ;
441
- const existing = cache . get ( path ) ;
442
- if ( existing ) {
443
- existing . refCount ++ ;
444
- }
445
- else {
446
- cache . set ( path , {
447
- watcher : watchFile (
448
- fileName ,
449
- ( fileName , eventKind , modifiedTime ) => forEach (
450
- callbacksCache . get ( path ) ,
451
- cb => cb ( fileName , eventKind , modifiedTime )
452
- ) ,
453
- pollingInterval ,
454
- options
455
- ) ,
456
- refCount : 1
457
- } ) ;
448
+ return {
449
+ close : ( ) => {
450
+ const watcher = cache . get ( path ) ;
451
+ // Watcher is not expected to be undefined, but if it is normally its because
452
+ // exception was thrown somewhere else and watch state is not what it should be
453
+ if ( ! watcher ) return ;
454
+ if ( ! orderedRemoveItem ( watcher . callbacks , callback ) || watcher . callbacks . length ) return ;
455
+ cache . delete ( path ) ;
456
+ closeFileWatcherOf ( watcher ) ;
458
457
}
459
- callbacksCache . add ( path , callback ) ;
460
-
461
- return {
462
- close : ( ) => {
463
- const watcher = Debug . checkDefined ( cache . get ( path ) ) ;
464
- callbacksCache . remove ( path , callback ) ;
465
- watcher . refCount -- ;
466
- if ( watcher . refCount ) return ;
467
- cache . delete ( path ) ;
468
- closeFileWatcherOf ( watcher ) ;
469
- }
470
- } ;
471
458
} ;
472
459
}
473
460
474
461
/**
475
462
* Returns true if file status changed
476
463
*/
477
- /*@internal */
478
- export function onWatchedFileStat ( watchedFile : WatchedFile , modifiedTime : Date ) : boolean {
464
+ function onWatchedFileStat ( watchedFile : WatchedFile , modifiedTime : Date ) : boolean {
479
465
const oldTime = watchedFile . mtime . getTime ( ) ;
480
466
const newTime = modifiedTime . getTime ( ) ;
481
467
if ( oldTime !== newTime ) {
@@ -512,8 +498,7 @@ namespace ts {
512
498
curSysLog = logger ;
513
499
}
514
500
515
- /*@internal */
516
- export interface RecursiveDirectoryWatcherHost {
501
+ interface RecursiveDirectoryWatcherHost {
517
502
watchDirectory : HostWatchDirectory ;
518
503
useCaseSensitiveFileNames : boolean ;
519
504
getCurrentDirectory : System [ "getCurrentDirectory" ] ;
@@ -529,8 +514,7 @@ namespace ts {
529
514
* that means if this is recursive watcher, watch the children directories as well
530
515
* (eg on OS that dont support recursive watch using fs.watch use fs.watchFile)
531
516
*/
532
- /*@internal */
533
- export function createDirectoryWatcherSupportingRecursive ( {
517
+ function createDirectoryWatcherSupportingRecursive ( {
534
518
watchDirectory,
535
519
useCaseSensitiveFileNames,
536
520
getCurrentDirectory,
@@ -792,8 +776,7 @@ namespace ts {
792
776
Directory ,
793
777
}
794
778
795
- /*@internal */
796
- export function createFileWatcherCallback ( callback : FsWatchCallback ) : FileWatcherCallback {
779
+ function createFileWatcherCallback ( callback : FsWatchCallback ) : FileWatcherCallback {
797
780
return ( _fileName , eventKind , modifiedTime ) => callback ( eventKind === FileWatcherEventKind . Changed ? "change" : "rename" , "" , modifiedTime ) ;
798
781
}
799
782
@@ -854,7 +837,7 @@ namespace ts {
854
837
/*@internal */
855
838
export interface CreateSystemWatchFunctions {
856
839
// Polling watch file
857
- pollingWatchFile : HostWatchFile ;
840
+ pollingWatchFileWorker : HostWatchFile ;
858
841
// For dynamic polling watch file
859
842
getModifiedTime : NonNullable < System [ "getModifiedTime" ] > ;
860
843
setTimeout : NonNullable < System [ "setTimeout" ] > ;
@@ -878,7 +861,7 @@ namespace ts {
878
861
879
862
/*@internal */
880
863
export function createSystemWatchFunctions ( {
881
- pollingWatchFile ,
864
+ pollingWatchFileWorker ,
882
865
getModifiedTime,
883
866
setTimeout,
884
867
clearTimeout,
@@ -896,6 +879,9 @@ namespace ts {
896
879
inodeWatching,
897
880
sysLog,
898
881
} : CreateSystemWatchFunctions ) : { watchFile : HostWatchFile ; watchDirectory : HostWatchDirectory ; } {
882
+ const pollingWatches = new Map < string , SingleFileWatcher < FileWatcherCallback > > ( ) ;
883
+ const fsWatches = new Map < string , SingleFileWatcher < FsWatchCallback > > ( ) ;
884
+ const fsWatchesRecursive = new Map < string , SingleFileWatcher < FsWatchCallback > > ( ) ;
899
885
let dynamicPollingWatchFile : HostWatchFile | undefined ;
900
886
let fixedChunkSizePollingWatchFile : HostWatchFile | undefined ;
901
887
let nonPollingWatchFile : HostWatchFile | undefined ;
@@ -968,7 +954,7 @@ namespace ts {
968
954
// Use notifications from FS to watch with falling back to fs.watchFile
969
955
generateWatchFileOptions ( WatchFileKind . UseFsEventsOnParentDirectory , PollingWatchKind . PriorityInterval , options ) :
970
956
// Default to do not use fixed polling interval
971
- { watchFile : defaultWatchFileKind ?.( ) || WatchFileKind . FixedPollingInterval } ;
957
+ { watchFile : defaultWatchFileKind ?.( ) || WatchFileKind . UseFsEvents } ;
972
958
}
973
959
}
974
960
@@ -1073,13 +1059,39 @@ namespace ts {
1073
1059
}
1074
1060
}
1075
1061
1062
+ function pollingWatchFile ( fileName : string , callback : FileWatcherCallback , pollingInterval : PollingInterval , options : WatchOptions | undefined ) {
1063
+ return createSingleWatcherPerName (
1064
+ pollingWatches ,
1065
+ useCaseSensitiveFileNames ,
1066
+ fileName ,
1067
+ callback ,
1068
+ cb => pollingWatchFileWorker ( fileName , cb , pollingInterval , options ) ,
1069
+ ) ;
1070
+ }
1076
1071
function fsWatch (
1077
1072
fileOrDirectory : string ,
1078
1073
entryKind : FileSystemEntryKind ,
1079
1074
callback : FsWatchCallback ,
1080
1075
recursive : boolean ,
1081
1076
fallbackPollingInterval : PollingInterval ,
1082
1077
fallbackOptions : WatchOptions | undefined
1078
+ ) : FileWatcher {
1079
+ return createSingleWatcherPerName (
1080
+ recursive ? fsWatchesRecursive : fsWatches ,
1081
+ useCaseSensitiveFileNames ,
1082
+ fileOrDirectory ,
1083
+ callback ,
1084
+ cb => fsWatchHandlingExistenceOnHost ( fileOrDirectory , entryKind , cb , recursive , fallbackPollingInterval , fallbackOptions ) ,
1085
+ ) ;
1086
+ }
1087
+
1088
+ function fsWatchHandlingExistenceOnHost (
1089
+ fileOrDirectory : string ,
1090
+ entryKind : FileSystemEntryKind ,
1091
+ callback : FsWatchCallback ,
1092
+ recursive : boolean ,
1093
+ fallbackPollingInterval : PollingInterval ,
1094
+ fallbackOptions : WatchOptions | undefined
1083
1095
) : FileWatcher {
1084
1096
let lastDirectoryPartWithDirectorySeparator : string | undefined ;
1085
1097
let lastDirectoryPart : string | undefined ;
@@ -1445,7 +1457,7 @@ namespace ts {
1445
1457
const fsSupportsRecursiveFsWatch = isNode4OrLater && ( process . platform === "win32" || process . platform === "darwin" ) ;
1446
1458
const getCurrentDirectory = memoize ( ( ) => process . cwd ( ) ) ;
1447
1459
const { watchFile, watchDirectory } = createSystemWatchFunctions ( {
1448
- pollingWatchFile : createSingleFileWatcherPerName ( fsWatchFileWorker , useCaseSensitiveFileNames ) ,
1460
+ pollingWatchFileWorker : fsWatchFileWorker ,
1449
1461
getModifiedTime,
1450
1462
setTimeout,
1451
1463
clearTimeout,
0 commit comments