@@ -332,9 +332,9 @@ namespace ts {
332
332
/*@internal */
333
333
export interface RecursiveDirectoryWatcherHost {
334
334
watchDirectory : HostWatchDirectory ;
335
+ useCaseSensitiveFileNames : boolean ;
335
336
getAccessibleSortedChildDirectories ( path : string ) : ReadonlyArray < string > ;
336
337
directoryExists ( dir : string ) : boolean ;
337
- filePathComparer : Comparer < string > ;
338
338
realpath ( s : string ) : string ;
339
339
}
340
340
@@ -345,60 +345,94 @@ namespace ts {
345
345
*/
346
346
/*@internal */
347
347
export function createRecursiveDirectoryWatcher ( host : RecursiveDirectoryWatcherHost ) : ( directoryName : string , callback : DirectoryWatcherCallback ) => FileWatcher {
348
- type ChildWatches = ReadonlyArray < DirectoryWatcher > ;
349
- interface DirectoryWatcher extends FileWatcher {
350
- childWatches : ChildWatches ;
348
+ interface ChildDirectoryWatcher extends FileWatcher {
351
349
dirName : string ;
352
350
}
351
+ type ChildWatches = ReadonlyArray < ChildDirectoryWatcher > ;
352
+ interface HostDirectoryWatcher {
353
+ watcher : FileWatcher ;
354
+ childWatches : ChildWatches ;
355
+ refCount : number ;
356
+ }
357
+
358
+ const cache = createMap < HostDirectoryWatcher > ( ) ;
359
+ const callbackCache = createMultiMap < DirectoryWatcherCallback > ( ) ;
360
+ const filePathComparer = getStringComparer ( ! host . useCaseSensitiveFileNames ) ;
361
+ const toCanonicalFilePath = createGetCanonicalFileName ( host . useCaseSensitiveFileNames ) ;
353
362
354
363
return createDirectoryWatcher ;
355
364
356
365
/**
357
366
* Create the directory watcher for the dirPath.
358
367
*/
359
- function createDirectoryWatcher ( dirName : string , callback : DirectoryWatcherCallback ) : DirectoryWatcher {
360
- const watcher = host . watchDirectory ( dirName , fileName => {
361
- // Call the actual callback
362
- callback ( fileName ) ;
368
+ function createDirectoryWatcher ( dirName : string , callback ?: DirectoryWatcherCallback ) : ChildDirectoryWatcher {
369
+ const dirPath = toCanonicalFilePath ( dirName ) as Path ;
370
+ let directoryWatcher = cache . get ( dirPath ) ;
371
+ if ( directoryWatcher ) {
372
+ directoryWatcher . refCount ++ ;
373
+ }
374
+ else {
375
+ directoryWatcher = {
376
+ watcher : host . watchDirectory ( dirName , fileName => {
377
+ // Call the actual callback
378
+ callbackCache . forEach ( ( callbacks , rootDirName ) => {
379
+ if ( rootDirName === dirPath || ( startsWith ( dirPath , rootDirName ) && dirPath [ rootDirName . length ] === directorySeparator ) ) {
380
+ callbacks . forEach ( callback => callback ( fileName ) ) ;
381
+ }
382
+ } ) ;
363
383
364
- // Iterate through existing children and update the watches if needed
365
- updateChildWatches ( result , callback ) ;
366
- } ) ;
384
+ // Iterate through existing children and update the watches if needed
385
+ updateChildWatches ( dirName , dirPath ) ;
386
+ } ) ,
387
+ refCount : 1 ,
388
+ childWatches : emptyArray
389
+ } ;
390
+ cache . set ( dirPath , directoryWatcher ) ;
391
+ updateChildWatches ( dirName , dirPath ) ;
392
+ }
367
393
368
- let result : DirectoryWatcher = {
369
- close : ( ) => {
370
- watcher . close ( ) ;
371
- result . childWatches . forEach ( closeFileWatcher ) ;
372
- result = undefined ! ;
373
- } ,
394
+ if ( callback ) {
395
+ callbackCache . add ( dirPath , callback ) ;
396
+ }
397
+
398
+ return {
374
399
dirName,
375
- childWatches : emptyArray
400
+ close : ( ) => {
401
+ const directoryWatcher = Debug . assertDefined ( cache . get ( dirPath ) ) ;
402
+ if ( callback ) callbackCache . remove ( dirPath , callback ) ;
403
+ directoryWatcher . refCount -- ;
404
+
405
+ if ( directoryWatcher . refCount ) return ;
406
+
407
+ cache . delete ( dirPath ) ;
408
+ closeFileWatcherOf ( directoryWatcher ) ;
409
+ directoryWatcher . childWatches . forEach ( closeFileWatcher ) ;
410
+ }
376
411
} ;
377
- updateChildWatches ( result , callback ) ;
378
- return result ;
379
412
}
380
413
381
- function updateChildWatches ( watcher : DirectoryWatcher , callback : DirectoryWatcherCallback ) {
414
+ function updateChildWatches ( dirName : string , dirPath : Path ) {
382
415
// Iterate through existing children and update the watches if needed
383
- if ( watcher ) {
384
- watcher . childWatches = watchChildDirectories ( watcher . dirName , watcher . childWatches , callback ) ;
416
+ const parentWatcher = cache . get ( dirPath ) ;
417
+ if ( parentWatcher ) {
418
+ parentWatcher . childWatches = watchChildDirectories ( dirName , parentWatcher . childWatches ) ;
385
419
}
386
420
}
387
421
388
422
/**
389
423
* Watch the directories in the parentDir
390
424
*/
391
- function watchChildDirectories ( parentDir : string , existingChildWatches : ChildWatches , callback : DirectoryWatcherCallback ) : ChildWatches {
392
- let newChildWatches : DirectoryWatcher [ ] | undefined ;
393
- enumerateInsertsAndDeletes < string , DirectoryWatcher > (
425
+ function watchChildDirectories ( parentDir : string , existingChildWatches : ChildWatches ) : ChildWatches {
426
+ let newChildWatches : ChildDirectoryWatcher [ ] | undefined ;
427
+ enumerateInsertsAndDeletes < string , ChildDirectoryWatcher > (
394
428
host . directoryExists ( parentDir ) ? mapDefined ( host . getAccessibleSortedChildDirectories ( parentDir ) , child => {
395
429
const childFullName = getNormalizedAbsolutePath ( child , parentDir ) ;
396
430
// Filter our the symbolic link directories since those arent included in recursive watch
397
431
// which is same behaviour when recursive: true is passed to fs.watch
398
- return host . filePathComparer ( childFullName , host . realpath ( childFullName ) ) === Comparison . EqualTo ? childFullName : undefined ;
432
+ return filePathComparer ( childFullName , normalizePath ( host . realpath ( childFullName ) ) ) === Comparison . EqualTo ? childFullName : undefined ;
399
433
} ) : emptyArray ,
400
434
existingChildWatches ,
401
- ( child , childWatcher ) => host . filePathComparer ( child , childWatcher . dirName ) ,
435
+ ( child , childWatcher ) => filePathComparer ( child , childWatcher . dirName ) ,
402
436
createAndAddChildDirectoryWatcher ,
403
437
closeFileWatcher ,
404
438
addChildDirectoryWatcher
@@ -410,14 +444,14 @@ namespace ts {
410
444
* Create new childDirectoryWatcher and add it to the new ChildDirectoryWatcher list
411
445
*/
412
446
function createAndAddChildDirectoryWatcher ( childName : string ) {
413
- const result = createDirectoryWatcher ( childName , callback ) ;
447
+ const result = createDirectoryWatcher ( childName ) ;
414
448
addChildDirectoryWatcher ( result ) ;
415
449
}
416
450
417
451
/**
418
452
* Add child directory watcher to the new ChildDirectoryWatcher list
419
453
*/
420
- function addChildDirectoryWatcher ( childWatcher : DirectoryWatcher ) {
454
+ function addChildDirectoryWatcher ( childWatcher : ChildDirectoryWatcher ) {
421
455
( newChildWatches || ( newChildWatches = [ ] ) ) . push ( childWatcher ) ;
422
456
}
423
457
}
@@ -710,7 +744,7 @@ namespace ts {
710
744
createWatchDirectoryUsing ( dynamicPollingWatchFile || createDynamicPriorityPollingWatchFile ( { getModifiedTime, setTimeout } ) ) :
711
745
watchDirectoryUsingFsWatch ;
712
746
const watchDirectoryRecursively = createRecursiveDirectoryWatcher ( {
713
- filePathComparer : getStringComparer ( ! useCaseSensitiveFileNames ) ,
747
+ useCaseSensitiveFileNames,
714
748
directoryExists,
715
749
getAccessibleSortedChildDirectories : path => getAccessibleFileSystemEntries ( path ) . directories ,
716
750
watchDirectory,
0 commit comments