33namespace ts {
44 export type FileWatcherCallback = ( fileName : string , removed ?: boolean ) => void ;
55 export type DirectoryWatcherCallback = ( directoryName : string ) => void ;
6+ export interface WatchedFile {
7+ fileName : string ;
8+ callback : FileWatcherCallback ;
9+ mtime ?: Date ;
10+ }
611
712 export interface System {
813 args : string [ ] ;
@@ -26,12 +31,6 @@ namespace ts {
2631 exit ( exitCode ?: number ) : void ;
2732 }
2833
29- interface WatchedFile {
30- fileName : string ;
31- callback : FileWatcherCallback ;
32- mtime ?: Date ;
33- }
34-
3534 export interface FileWatcher {
3635 close ( ) : void ;
3736 }
@@ -230,83 +229,7 @@ namespace ts {
230229 const _os = require ( "os" ) ;
231230 const _crypto = require ( "crypto" ) ;
232231
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" ] ;
310233
311234 function createWatchedFileSet ( ) {
312235 const dirWatchers : Map < DirectoryWatcher > = { } ;
@@ -389,26 +312,11 @@ namespace ts {
389312 }
390313 }
391314 }
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 ( ) ;
407315 const watchedFileSet = createWatchedFileSet ( ) ;
408316
409317 function isNode4OrLater ( ) : boolean {
410- return parseInt ( process . version . charAt ( 1 ) ) >= 4 ;
411- }
318+ return parseInt ( process . version . charAt ( 1 ) ) >= 4 ;
319+ }
412320
413321 const platform : string = _os . platform ( ) ;
414322 // win32\win64 are case insensitive platforms, MacOS (darwin) by default is also case insensitive
@@ -501,7 +409,7 @@ namespace ts {
501409 const files = _fs . readdirSync ( path || "." ) . sort ( ) ;
502410 const directories : string [ ] = [ ] ;
503411 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
505413 // "." and "..". See https://github.com/nodejs/node/issues/4002
506414 if ( current === "." || current === ".." ) {
507415 continue ;
@@ -535,15 +443,26 @@ namespace ts {
535443 readFile,
536444 writeFile,
537445 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+ }
547466 } ,
548467 watchDirectory : ( directoryName , callback , recursive ) => {
549468 // Node 4.0 `fs.watch` function supports the "recursive" option on both OSX and Windows
0 commit comments