@@ -57,6 +57,11 @@ namespace ts {
5757 /* @internal */
5858 export const missingFileModifiedTime = new Date ( 0 ) ; // Any subsequent modification will occur after this time
5959
60+ /* @internal */
61+ export function getModifiedTime ( host : { getModifiedTime : NonNullable < System [ "getModifiedTime" ] > ; } , fileName : string ) {
62+ return host . getModifiedTime ( fileName ) || missingFileModifiedTime ;
63+ }
64+
6065 interface Levels {
6166 Low : number ;
6267 Medium : number ;
@@ -126,6 +131,64 @@ namespace ts {
126131 }
127132 }
128133
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+
129192 /* @internal */
130193 export function createDynamicPriorityPollingWatchFile ( host : {
131194 getModifiedTime : NonNullable < System [ "getModifiedTime" ] > ;
@@ -154,7 +217,7 @@ namespace ts {
154217 fileName,
155218 callback,
156219 unchangedPolls : 0 ,
157- mtime : getModifiedTime ( fileName )
220+ mtime : getModifiedTime ( host , fileName )
158221 } ;
159222 watchedFiles . push ( file ) ;
160223
@@ -203,26 +266,16 @@ namespace ts {
203266 }
204267
205268 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+ ) ;
218276
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 ) {
226279 watchedFile . unchangedPolls = 0 ;
227280 // Changed files go to changedFilesInLastPoll queue
228281 if ( queue !== changedFilesInLastPoll ) {
@@ -244,30 +297,6 @@ namespace ts {
244297 queue [ pollIndex ] = undefined ;
245298 addToPollingIntervalQueue ( watchedFile , pollingInterval === PollingInterval . Low ? PollingInterval . Medium : PollingInterval . High ) ;
246299 }
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- }
271300 }
272301 }
273302
@@ -301,10 +330,6 @@ namespace ts {
301330 function scheduleNextPoll ( pollingInterval : PollingInterval ) {
302331 pollingIntervalQueue ( pollingInterval ) . pollScheduled = host . setTimeout ( pollingInterval === PollingInterval . Low ? pollLowPollingIntervalQueue : pollPollingIntervalQueue , pollingInterval , pollingIntervalQueue ( pollingInterval ) ) ;
303332 }
304-
305- function getModifiedTime ( fileName : string ) {
306- return host . getModifiedTime ( fileName ) || missingFileModifiedTime ;
307- }
308333 }
309334
310335 function createUseFsEventsOnParentDirectoryWatchFile ( fsWatch : FsWatch , useCaseSensitiveFileNames : boolean ) : HostWatchFile {
@@ -361,6 +386,43 @@ namespace ts {
361386 }
362387 }
363388
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+
364426 /* @internal */
365427 export function createSingleFileWatcherPerName (
366428 watchFile : HostWatchFile ,
@@ -795,6 +857,7 @@ namespace ts {
795857 tscWatchFile : string | undefined ;
796858 useNonPollingWatchers ?: boolean ;
797859 tscWatchDirectory : string | undefined ;
860+ defaultWatchFileKind : System [ "defaultWatchFileKind" ] ;
798861 }
799862
800863 /*@internal */
@@ -814,8 +877,10 @@ namespace ts {
814877 tscWatchFile,
815878 useNonPollingWatchers,
816879 tscWatchDirectory,
880+ defaultWatchFileKind,
817881 } : CreateSystemWatchFunctions ) : { watchFile : HostWatchFile ; watchDirectory : HostWatchDirectory ; } {
818882 let dynamicPollingWatchFile : HostWatchFile | undefined ;
883+ let fixedChunkSizePollingWatchFile : HostWatchFile | undefined ;
819884 let nonPollingWatchFile : HostWatchFile | undefined ;
820885 let hostRecursiveDirectoryWatcher : HostWatchDirectory | undefined ;
821886 return {
@@ -833,6 +898,8 @@ namespace ts {
833898 return pollingWatchFile ( fileName , callback , pollingInterval , /*options*/ undefined ) ;
834899 case WatchFileKind . DynamicPriorityPolling :
835900 return ensureDynamicPollingWatchFile ( ) ( fileName , callback , pollingInterval , /*options*/ undefined ) ;
901+ case WatchFileKind . FixedChunkSizePolling :
902+ return ensureFixedChunkSizePollingWatchFile ( ) ( fileName , callback , /* pollingInterval */ undefined ! , /*options*/ undefined ) ;
836903 case WatchFileKind . UseFsEvents :
837904 return fsWatch (
838905 fileName ,
@@ -853,8 +920,11 @@ namespace ts {
853920 }
854921
855922 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 } ) ;
858928 }
859929
860930 function updateOptionsForWatchFile ( options : WatchOptions | undefined , useNonPollingWatchers ?: boolean ) : WatchOptions {
@@ -880,7 +950,7 @@ namespace ts {
880950 // Use notifications from FS to watch with falling back to fs.watchFile
881951 generateWatchFileOptions ( WatchFileKind . UseFsEventsOnParentDirectory , PollingWatchKind . PriorityInterval , options ) :
882952 // Default to do not use fixed polling interval
883- { watchFile : WatchFileKind . FixedPollingInterval } ;
953+ { watchFile : defaultWatchFileKind ?. ( ) || WatchFileKind . FixedPollingInterval } ;
884954 }
885955 }
886956
@@ -944,6 +1014,13 @@ namespace ts {
9441014 PollingInterval . Medium ,
9451015 /*options*/ undefined
9461016 ) ;
1017+ case WatchDirectoryKind . FixedChunkSizePolling :
1018+ return ensureFixedChunkSizePollingWatchFile ( ) (
1019+ directoryName ,
1020+ ( ) => callback ( directoryName ) ,
1021+ /* pollingInterval */ undefined ! ,
1022+ /*options*/ undefined
1023+ ) ;
9471024 case WatchDirectoryKind . UseFsEvents :
9481025 return fsWatch (
9491026 directoryName ,
@@ -1131,6 +1208,7 @@ namespace ts {
11311208 // For testing
11321209 /*@internal */ now ?( ) : Date ;
11331210 /*@internal */ require ?( baseDir : string , moduleName : string ) : RequireResult ;
1211+ /*@internal */ defaultWatchFileKind ?( ) : WatchFileKind | undefined ;
11341212 }
11351213
11361214 export interface FileWatcher {
@@ -1219,6 +1297,7 @@ namespace ts {
12191297 tscWatchFile : process . env . TSC_WATCHFILE ,
12201298 useNonPollingWatchers : process . env . TSC_NONPOLLING_WATCHER ,
12211299 tscWatchDirectory : process . env . TSC_WATCHDIRECTORY ,
1300+ defaultWatchFileKind : ( ) => sys ! . defaultWatchFileKind ?.( ) ,
12221301 } ) ;
12231302 const nodeSystem : System = {
12241303 args : process . argv . slice ( 2 ) ,
0 commit comments