@@ -57,6 +57,11 @@ namespace ts {
57
57
/* @internal */
58
58
export const missingFileModifiedTime = new Date ( 0 ) ; // Any subsequent modification will occur after this time
59
59
60
+ /* @internal */
61
+ export function getModifiedTime ( host : { getModifiedTime : NonNullable < System [ "getModifiedTime" ] > ; } , fileName : string ) {
62
+ return host . getModifiedTime ( fileName ) || missingFileModifiedTime ;
63
+ }
64
+
60
65
interface Levels {
61
66
Low : number ;
62
67
Medium : number ;
@@ -126,6 +131,64 @@ namespace ts {
126
131
}
127
132
}
128
133
134
+ interface WatchedFileWithIsClosed extends WatchedFile {
135
+ isClosed ?: boolean ;
136
+ }
137
+ function pollWatchedFileQueue < T extends WatchedFileWithIsClosed > (
138
+ host : { getModifiedTime : NonNullable < System [ "getModifiedTime" ] > ; } ,
139
+ queue : ( T | undefined ) [ ] ,
140
+ pollIndex : number , chunkSize : number ,
141
+ callbackOnWatchFileStat ?: ( watchedFile : T , pollIndex : number , fileChanged : boolean ) => void
142
+ ) {
143
+ let definedValueCopyToIndex = pollIndex ;
144
+ // Max visit would be all elements of the queue
145
+ for ( let canVisit = queue . length ; chunkSize && canVisit ; nextPollIndex ( ) , canVisit -- ) {
146
+ const watchedFile = queue [ pollIndex ] ;
147
+ if ( ! watchedFile ) {
148
+ continue ;
149
+ }
150
+ else if ( watchedFile . isClosed ) {
151
+ queue [ pollIndex ] = undefined ;
152
+ continue ;
153
+ }
154
+
155
+ // Only files polled count towards chunkSize
156
+ chunkSize -- ;
157
+ const fileChanged = onWatchedFileStat ( watchedFile , getModifiedTime ( host , watchedFile . fileName ) ) ;
158
+ if ( watchedFile . isClosed ) {
159
+ // Closed watcher as part of callback
160
+ queue [ pollIndex ] = undefined ;
161
+ continue ;
162
+ }
163
+
164
+ callbackOnWatchFileStat ?.( watchedFile , pollIndex , fileChanged ) ;
165
+ // Defragment the queue while we are at it
166
+ if ( queue [ pollIndex ] ) {
167
+ // Copy this file to the non hole location
168
+ if ( definedValueCopyToIndex < pollIndex ) {
169
+ queue [ definedValueCopyToIndex ] = watchedFile ;
170
+ queue [ pollIndex ] = undefined ;
171
+ }
172
+ definedValueCopyToIndex ++ ;
173
+ }
174
+ }
175
+
176
+ // Return next poll index
177
+ return pollIndex ;
178
+
179
+ function nextPollIndex ( ) {
180
+ pollIndex ++ ;
181
+ if ( pollIndex === queue . length ) {
182
+ if ( definedValueCopyToIndex < pollIndex ) {
183
+ // There are holes from definedValueCopyToIndex to end of queue, change queue size
184
+ queue . length = definedValueCopyToIndex ;
185
+ }
186
+ pollIndex = 0 ;
187
+ definedValueCopyToIndex = 0 ;
188
+ }
189
+ }
190
+ }
191
+
129
192
/* @internal */
130
193
export function createDynamicPriorityPollingWatchFile ( host : {
131
194
getModifiedTime : NonNullable < System [ "getModifiedTime" ] > ;
@@ -154,7 +217,7 @@ namespace ts {
154
217
fileName,
155
218
callback,
156
219
unchangedPolls : 0 ,
157
- mtime : getModifiedTime ( fileName )
220
+ mtime : getModifiedTime ( host , fileName )
158
221
} ;
159
222
watchedFiles . push ( file ) ;
160
223
@@ -203,26 +266,16 @@ namespace ts {
203
266
}
204
267
205
268
function pollQueue ( queue : ( WatchedFile | undefined ) [ ] , pollingInterval : PollingInterval , pollIndex : number , chunkSize : number ) {
206
- // Max visit would be all elements of the queue
207
- let needsVisit = queue . length ;
208
- let definedValueCopyToIndex = pollIndex ;
209
- for ( let polled = 0 ; polled < chunkSize && needsVisit > 0 ; nextPollIndex ( ) , needsVisit -- ) {
210
- const watchedFile = queue [ pollIndex ] ;
211
- if ( ! watchedFile ) {
212
- continue ;
213
- }
214
- else if ( watchedFile . isClosed ) {
215
- queue [ pollIndex ] = undefined ;
216
- continue ;
217
- }
269
+ return pollWatchedFileQueue (
270
+ host ,
271
+ queue ,
272
+ pollIndex ,
273
+ chunkSize ,
274
+ onWatchFileStat
275
+ ) ;
218
276
219
- polled ++ ;
220
- const fileChanged = onWatchedFileStat ( watchedFile , getModifiedTime ( watchedFile . fileName ) ) ;
221
- if ( watchedFile . isClosed ) {
222
- // Closed watcher as part of callback
223
- queue [ pollIndex ] = undefined ;
224
- }
225
- else if ( fileChanged ) {
277
+ function onWatchFileStat ( watchedFile : WatchedFile , pollIndex : number , fileChanged : boolean ) {
278
+ if ( fileChanged ) {
226
279
watchedFile . unchangedPolls = 0 ;
227
280
// Changed files go to changedFilesInLastPoll queue
228
281
if ( queue !== changedFilesInLastPoll ) {
@@ -244,30 +297,6 @@ namespace ts {
244
297
queue [ pollIndex ] = undefined ;
245
298
addToPollingIntervalQueue ( watchedFile , pollingInterval === PollingInterval . Low ? PollingInterval . Medium : PollingInterval . High ) ;
246
299
}
247
-
248
- if ( queue [ pollIndex ] ) {
249
- // Copy this file to the non hole location
250
- if ( definedValueCopyToIndex < pollIndex ) {
251
- queue [ definedValueCopyToIndex ] = watchedFile ;
252
- queue [ pollIndex ] = undefined ;
253
- }
254
- definedValueCopyToIndex ++ ;
255
- }
256
- }
257
-
258
- // Return next poll index
259
- return pollIndex ;
260
-
261
- function nextPollIndex ( ) {
262
- pollIndex ++ ;
263
- if ( pollIndex === queue . length ) {
264
- if ( definedValueCopyToIndex < pollIndex ) {
265
- // There are holes from nextDefinedValueIndex to end of queue, change queue size
266
- queue . length = definedValueCopyToIndex ;
267
- }
268
- pollIndex = 0 ;
269
- definedValueCopyToIndex = 0 ;
270
- }
271
300
}
272
301
}
273
302
@@ -301,10 +330,6 @@ namespace ts {
301
330
function scheduleNextPoll ( pollingInterval : PollingInterval ) {
302
331
pollingIntervalQueue ( pollingInterval ) . pollScheduled = host . setTimeout ( pollingInterval === PollingInterval . Low ? pollLowPollingIntervalQueue : pollPollingIntervalQueue , pollingInterval , pollingIntervalQueue ( pollingInterval ) ) ;
303
332
}
304
-
305
- function getModifiedTime ( fileName : string ) {
306
- return host . getModifiedTime ( fileName ) || missingFileModifiedTime ;
307
- }
308
333
}
309
334
310
335
function createUseFsEventsOnParentDirectoryWatchFile ( fsWatch : FsWatch , useCaseSensitiveFileNames : boolean ) : HostWatchFile {
@@ -361,6 +386,43 @@ namespace ts {
361
386
}
362
387
}
363
388
389
+ function createFixedChunkSizePollingWatchFile ( host : {
390
+ getModifiedTime : NonNullable < System [ "getModifiedTime" ] > ;
391
+ setTimeout : NonNullable < System [ "setTimeout" ] > ;
392
+ } ) : HostWatchFile {
393
+ const watchedFiles : ( WatchedFileWithIsClosed | undefined ) [ ] = [ ] ;
394
+ let pollIndex = 0 ;
395
+ let pollScheduled : any ;
396
+ return watchFile ;
397
+
398
+ function watchFile ( fileName : string , callback : FileWatcherCallback ) : FileWatcher {
399
+ const file : WatchedFileWithIsClosed = {
400
+ fileName,
401
+ callback,
402
+ mtime : getModifiedTime ( host , fileName )
403
+ } ;
404
+ watchedFiles . push ( file ) ;
405
+ scheduleNextPoll ( ) ;
406
+ return {
407
+ close : ( ) => {
408
+ file . isClosed = true ;
409
+ unorderedRemoveItem ( watchedFiles , file ) ;
410
+ }
411
+ } ;
412
+ }
413
+
414
+ function pollQueue ( ) {
415
+ pollScheduled = undefined ;
416
+ pollIndex = pollWatchedFileQueue ( host , watchedFiles , pollIndex , pollingChunkSize [ PollingInterval . Low ] ) ;
417
+ scheduleNextPoll ( ) ;
418
+ }
419
+
420
+ function scheduleNextPoll ( ) {
421
+ if ( ! watchedFiles . length || pollScheduled ) return ;
422
+ pollScheduled = host . setTimeout ( pollQueue , PollingInterval . High ) ;
423
+ }
424
+ }
425
+
364
426
/* @internal */
365
427
export function createSingleFileWatcherPerName (
366
428
watchFile : HostWatchFile ,
@@ -795,6 +857,7 @@ namespace ts {
795
857
tscWatchFile : string | undefined ;
796
858
useNonPollingWatchers ?: boolean ;
797
859
tscWatchDirectory : string | undefined ;
860
+ defaultWatchFileKind : System [ "defaultWatchFileKind" ] ;
798
861
}
799
862
800
863
/*@internal */
@@ -814,8 +877,10 @@ namespace ts {
814
877
tscWatchFile,
815
878
useNonPollingWatchers,
816
879
tscWatchDirectory,
880
+ defaultWatchFileKind,
817
881
} : CreateSystemWatchFunctions ) : { watchFile : HostWatchFile ; watchDirectory : HostWatchDirectory ; } {
818
882
let dynamicPollingWatchFile : HostWatchFile | undefined ;
883
+ let fixedChunkSizePollingWatchFile : HostWatchFile | undefined ;
819
884
let nonPollingWatchFile : HostWatchFile | undefined ;
820
885
let hostRecursiveDirectoryWatcher : HostWatchDirectory | undefined ;
821
886
return {
@@ -833,6 +898,8 @@ namespace ts {
833
898
return pollingWatchFile ( fileName , callback , pollingInterval , /*options*/ undefined ) ;
834
899
case WatchFileKind . DynamicPriorityPolling :
835
900
return ensureDynamicPollingWatchFile ( ) ( fileName , callback , pollingInterval , /*options*/ undefined ) ;
901
+ case WatchFileKind . FixedChunkSizePolling :
902
+ return ensureFixedChunkSizePollingWatchFile ( ) ( fileName , callback , /* pollingInterval */ undefined ! , /*options*/ undefined ) ;
836
903
case WatchFileKind . UseFsEvents :
837
904
return fsWatch (
838
905
fileName ,
@@ -853,8 +920,11 @@ namespace ts {
853
920
}
854
921
855
922
function ensureDynamicPollingWatchFile ( ) {
856
- return dynamicPollingWatchFile ||
857
- ( dynamicPollingWatchFile = createDynamicPriorityPollingWatchFile ( { getModifiedTime, setTimeout } ) ) ;
923
+ return dynamicPollingWatchFile ||= createDynamicPriorityPollingWatchFile ( { getModifiedTime, setTimeout } ) ;
924
+ }
925
+
926
+ function ensureFixedChunkSizePollingWatchFile ( ) {
927
+ return fixedChunkSizePollingWatchFile ||= createFixedChunkSizePollingWatchFile ( { getModifiedTime, setTimeout } ) ;
858
928
}
859
929
860
930
function updateOptionsForWatchFile ( options : WatchOptions | undefined , useNonPollingWatchers ?: boolean ) : WatchOptions {
@@ -880,7 +950,7 @@ namespace ts {
880
950
// Use notifications from FS to watch with falling back to fs.watchFile
881
951
generateWatchFileOptions ( WatchFileKind . UseFsEventsOnParentDirectory , PollingWatchKind . PriorityInterval , options ) :
882
952
// Default to do not use fixed polling interval
883
- { watchFile : WatchFileKind . FixedPollingInterval } ;
953
+ { watchFile : defaultWatchFileKind ?. ( ) || WatchFileKind . FixedPollingInterval } ;
884
954
}
885
955
}
886
956
@@ -944,6 +1014,13 @@ namespace ts {
944
1014
PollingInterval . Medium ,
945
1015
/*options*/ undefined
946
1016
) ;
1017
+ case WatchDirectoryKind . FixedChunkSizePolling :
1018
+ return ensureFixedChunkSizePollingWatchFile ( ) (
1019
+ directoryName ,
1020
+ ( ) => callback ( directoryName ) ,
1021
+ /* pollingInterval */ undefined ! ,
1022
+ /*options*/ undefined
1023
+ ) ;
947
1024
case WatchDirectoryKind . UseFsEvents :
948
1025
return fsWatch (
949
1026
directoryName ,
@@ -1131,6 +1208,7 @@ namespace ts {
1131
1208
// For testing
1132
1209
/*@internal */ now ?( ) : Date ;
1133
1210
/*@internal */ require ?( baseDir : string , moduleName : string ) : RequireResult ;
1211
+ /*@internal */ defaultWatchFileKind ?( ) : WatchFileKind | undefined ;
1134
1212
}
1135
1213
1136
1214
export interface FileWatcher {
@@ -1219,6 +1297,7 @@ namespace ts {
1219
1297
tscWatchFile : process . env . TSC_WATCHFILE ,
1220
1298
useNonPollingWatchers : process . env . TSC_NONPOLLING_WATCHER ,
1221
1299
tscWatchDirectory : process . env . TSC_WATCHDIRECTORY ,
1300
+ defaultWatchFileKind : ( ) => sys ! . defaultWatchFileKind ?.( ) ,
1222
1301
} ) ;
1223
1302
const nodeSystem : System = {
1224
1303
args : process . argv . slice ( 2 ) ,
0 commit comments