3
3
namespace ts {
4
4
export type FileWatcherCallback = ( fileName : string , removed ?: boolean ) => void ;
5
5
export type DirectoryWatcherCallback = ( directoryName : string ) => void ;
6
+ export interface WatchedFile {
7
+ fileName : string ;
8
+ callback : FileWatcherCallback ;
9
+ mtime ?: Date ;
10
+ }
6
11
7
12
export interface System {
8
13
args : string [ ] ;
@@ -26,12 +31,6 @@ namespace ts {
26
31
exit ( exitCode ?: number ) : void ;
27
32
}
28
33
29
- interface WatchedFile {
30
- fileName : string ;
31
- callback : FileWatcherCallback ;
32
- mtime ?: Date ;
33
- }
34
-
35
34
export interface FileWatcher {
36
35
close ( ) : void ;
37
36
}
@@ -230,83 +229,7 @@ namespace ts {
230
229
const _os = require ( "os" ) ;
231
230
const _crypto = require ( "crypto" ) ;
232
231
233
- // average async stat takes about 30 microseconds
234
- // set chunk size to do 30 files in < 1 millisecond
235
- function createPollingWatchedFileSet ( interval = 2500 , chunkSize = 30 ) {
236
- let watchedFiles : WatchedFile [ ] = [ ] ;
237
- let nextFileToCheck = 0 ;
238
- let watchTimer : any ;
239
-
240
- function getModifiedTime ( fileName : string ) : Date {
241
- return _fs . statSync ( fileName ) . mtime ;
242
- }
243
-
244
- function poll ( checkedIndex : number ) {
245
- const watchedFile = watchedFiles [ checkedIndex ] ;
246
- if ( ! watchedFile ) {
247
- return ;
248
- }
249
-
250
- _fs . stat ( watchedFile . fileName , ( err : any , stats : any ) => {
251
- if ( err ) {
252
- watchedFile . callback ( watchedFile . fileName ) ;
253
- }
254
- else if ( watchedFile . mtime . getTime ( ) !== stats . mtime . getTime ( ) ) {
255
- watchedFile . mtime = getModifiedTime ( watchedFile . fileName ) ;
256
- watchedFile . callback ( watchedFile . fileName , watchedFile . mtime . getTime ( ) === 0 ) ;
257
- }
258
- } ) ;
259
- }
260
-
261
- // this implementation uses polling and
262
- // stat due to inconsistencies of fs.watch
263
- // and efficiency of stat on modern filesystems
264
- function startWatchTimer ( ) {
265
- watchTimer = setInterval ( ( ) => {
266
- let count = 0 ;
267
- let nextToCheck = nextFileToCheck ;
268
- let firstCheck = - 1 ;
269
- while ( ( count < chunkSize ) && ( nextToCheck !== firstCheck ) ) {
270
- poll ( nextToCheck ) ;
271
- if ( firstCheck < 0 ) {
272
- firstCheck = nextToCheck ;
273
- }
274
- nextToCheck ++ ;
275
- if ( nextToCheck === watchedFiles . length ) {
276
- nextToCheck = 0 ;
277
- }
278
- count ++ ;
279
- }
280
- nextFileToCheck = nextToCheck ;
281
- } , interval ) ;
282
- }
283
-
284
- function addFile ( fileName : string , callback : FileWatcherCallback ) : WatchedFile {
285
- const file : WatchedFile = {
286
- fileName,
287
- callback,
288
- mtime : getModifiedTime ( fileName )
289
- } ;
290
-
291
- watchedFiles . push ( file ) ;
292
- if ( watchedFiles . length === 1 ) {
293
- startWatchTimer ( ) ;
294
- }
295
- return file ;
296
- }
297
-
298
- function removeFile ( file : WatchedFile ) {
299
- watchedFiles = copyListRemovingItem ( file , watchedFiles ) ;
300
- }
301
-
302
- return {
303
- getModifiedTime : getModifiedTime ,
304
- poll : poll ,
305
- startWatchTimer : startWatchTimer ,
306
- addFile : addFile ,
307
- removeFile : removeFile
308
- } ;
309
- }
232
+ const useNonPollingWatchers = process . env [ "TSC_NONPOLLING_WATCHER" ] ;
310
233
311
234
function createWatchedFileSet ( ) {
312
235
const dirWatchers : Map < DirectoryWatcher > = { } ;
@@ -389,26 +312,11 @@ namespace ts {
389
312
}
390
313
}
391
314
}
392
-
393
- // REVIEW: for now this implementation uses polling.
394
- // The advantage of polling is that it works reliably
395
- // on all os and with network mounted files.
396
- // For 90 referenced files, the average time to detect
397
- // changes is 2*msInterval (by default 5 seconds).
398
- // The overhead of this is .04 percent (1/2500) with
399
- // average pause of < 1 millisecond (and max
400
- // pause less than 1.5 milliseconds); question is
401
- // do we anticipate reference sets in the 100s and
402
- // do we care about waiting 10-20 seconds to detect
403
- // changes for large reference sets? If so, do we want
404
- // to increase the chunk size or decrease the interval
405
- // time dynamically to match the large reference set?
406
- const pollingWatchedFileSet = createPollingWatchedFileSet ( ) ;
407
315
const watchedFileSet = createWatchedFileSet ( ) ;
408
316
409
317
function isNode4OrLater ( ) : boolean {
410
- return parseInt ( process . version . charAt ( 1 ) ) >= 4 ;
411
- }
318
+ return parseInt ( process . version . charAt ( 1 ) ) >= 4 ;
319
+ }
412
320
413
321
const platform : string = _os . platform ( ) ;
414
322
// win32\win64 are case insensitive platforms, MacOS (darwin) by default is also case insensitive
@@ -501,7 +409,7 @@ namespace ts {
501
409
const files = _fs . readdirSync ( path || "." ) . sort ( ) ;
502
410
const directories : string [ ] = [ ] ;
503
411
for ( const current of files ) {
504
- // This is necessary because on some file system node fails to exclude
412
+ // This is necessary because on some file system node fails to exclude
505
413
// "." and "..". See https://github.com/nodejs/node/issues/4002
506
414
if ( current === "." || current === ".." ) {
507
415
continue ;
@@ -535,15 +443,26 @@ namespace ts {
535
443
readFile,
536
444
writeFile,
537
445
watchFile : ( fileName , callback ) => {
538
- // Node 4.0 stabilized the `fs.watch` function on Windows which avoids polling
539
- // and is more efficient than `fs.watchFile` (ref: https://github.com/nodejs/node/pull/2649
540
- // and https://github.com/Microsoft/TypeScript/issues/4643), therefore
541
- // if the current node.js version is newer than 4, use `fs.watch` instead.
542
- const watchSet = isNode4OrLater ( ) ? watchedFileSet : pollingWatchedFileSet ;
543
- const watchedFile = watchSet . addFile ( fileName , callback ) ;
544
- return {
545
- close : ( ) => watchSet . removeFile ( watchedFile )
546
- } ;
446
+ if ( useNonPollingWatchers ) {
447
+ const watchedFile = watchedFileSet . addFile ( fileName , callback ) ;
448
+ return {
449
+ close : ( ) => watchedFileSet . removeFile ( watchedFile )
450
+ } ;
451
+ }
452
+ else {
453
+ _fs . watchFile ( fileName , { persistent : true , interval : 250 } , fileChanged ) ;
454
+ return {
455
+ close : ( ) => _fs . unwatchFile ( fileName , fileChanged )
456
+ } ;
457
+ }
458
+
459
+ function fileChanged ( curr : any , prev : any ) {
460
+ if ( + curr . mtime <= + prev . mtime ) {
461
+ return ;
462
+ }
463
+
464
+ callback ( fileName ) ;
465
+ }
547
466
} ,
548
467
watchDirectory : ( directoryName , callback , recursive ) => {
549
468
// Node 4.0 `fs.watch` function supports the "recursive" option on both OSX and Windows
0 commit comments