@@ -307,15 +307,21 @@ namespace ts {
307307 function createWatchedFileSet ( ) {
308308 const dirWatchers = createFileMap < DirWatcher > ( ) ;
309309 const recursiveDirWatchers = createFileMap < DirWatcher > ( ) ;
310- const fileWatcherCallbacks = createFileMap < FileWatcherCallback > ( ) ;
311- const dirWatcherCallbacks = createFileMap < DirWatcherCallback > ( ) ;
310+ // One file can have multiple watchers
311+ const fileWatcherCallbacks = createFileMap < FileWatcherCallback [ ] > ( ) ;
312+ const dirWatcherCallbacks = createFileMap < DirWatcherCallback [ ] > ( ) ;
312313
313314 const currentDirectory = process . cwd ( ) ;
314315 return { addFile, removeFile, addDir } ;
315316
316317 function addDir ( dirName : string , callback : DirWatcherCallback , recursive ?: boolean ) {
317318 const dirPath = toPath ( dirName , currentDirectory , getCanonicalPath ) ;
318- dirWatcherCallbacks . set ( dirPath , callback ) ;
319+ if ( ! dirWatcherCallbacks . contains ( dirPath ) ) {
320+ dirWatcherCallbacks . set ( dirPath , [ callback ] ) ;
321+ }
322+ else {
323+ dirWatcherCallbacks . get ( dirPath ) . push ( callback ) ;
324+ }
319325 const { watcher, isRecursive } = addDirWatcher ( dirPath , recursive ) ;
320326 return {
321327 close : ( ) => reduceDirWatcherRefCount ( watcher , dirPath , isRecursive )
@@ -341,7 +347,8 @@ namespace ts {
341347
342348 // Node 4.0 `fs.watch` function supports the "recursive" option on both OSX and Windows
343349 // (ref: https://github.com/nodejs/node/pull/2649 and https://github.com/Microsoft/TypeScript/issues/4643)
344- if ( isNode4OrLater ( ) && recursive === true ) {
350+ if ( isNode4OrLater ( ) && recursive === true &&
351+ ( process . platform === "win32" || process . platform === "darwin" ) ) {
345352 if ( recursiveDirWatchers . contains ( dirPath ) ) {
346353 const watcher = recursiveDirWatchers . get ( dirPath ) ;
347354 watcher . referenceCount += 1 ;
@@ -357,12 +364,13 @@ namespace ts {
357364 return { watcher, isRecursive : false } ;
358365 }
359366 watchers = dirWatchers ;
367+ options . recursive = false ;
360368 }
361369
362370 const watcher : DirWatcher = _fs . watch ( dirPath , options , ( eventName : string , relativeFileName : string ) => fileEventHandler ( eventName , relativeFileName , dirPath ) ) ;
363371 watcher . referenceCount = 1 ;
364372 watchers . set ( dirPath , watcher ) ;
365- return { watcher, isRecursive : false } ;
373+ return { watcher, isRecursive : options . recursive } ;
366374 }
367375
368376 function findDirWatcherForFile ( filePath : Path ) : { watcher : DirWatcher , watcherPath : Path , isRecursive : boolean } {
@@ -389,24 +397,37 @@ namespace ts {
389397
390398 function addFile ( fileName : string , callback : FileWatcherCallback ) : WatchedFile {
391399 const filePath = toPath ( fileName , currentDirectory , getCanonicalPath ) ;
392- const { watcher } = findDirWatcherForFile ( filePath ) ;
393- if ( ! watcher ) {
394- addDirWatcher ( getDirectoryPath ( filePath ) ) ;
400+
401+ if ( fileWatcherCallbacks . contains ( filePath ) ) {
402+ fileWatcherCallbacks . get ( filePath ) . push ( callback ) ;
395403 }
396404 else {
397- watcher . referenceCount += 1 ;
405+ const { watcher } = findDirWatcherForFile ( filePath ) ;
406+ if ( ! watcher ) {
407+ addDirWatcher ( getDirectoryPath ( filePath ) ) ;
408+ }
409+ else {
410+ watcher . referenceCount += 1 ;
411+ }
412+ fileWatcherCallbacks . set ( filePath , [ callback ] ) ;
398413 }
399- fileWatcherCallbacks . set ( filePath , callback ) ;
400414 return { fileName, callback } ;
401415 }
402416
403417 function removeFile ( file : WatchedFile ) {
404418 const filePath = toPath ( file . fileName , currentDirectory , getCanonicalPath ) ;
405- fileWatcherCallbacks . remove ( filePath ) ;
406-
407- const { watcher, watcherPath, isRecursive } = findDirWatcherForFile ( filePath ) ;
408- if ( watcher ) {
409- reduceDirWatcherRefCount ( watcher , watcherPath , isRecursive ) ;
419+ if ( fileWatcherCallbacks . contains ( filePath ) ) {
420+ const newCallbacks = copyListRemovingItem ( file . callback , fileWatcherCallbacks . get ( filePath ) ) ;
421+ if ( newCallbacks . length === 0 ) {
422+ fileWatcherCallbacks . remove ( filePath ) ;
423+ const { watcher, watcherPath, isRecursive } = findDirWatcherForFile ( filePath ) ;
424+ if ( watcher ) {
425+ reduceDirWatcherRefCount ( watcher , watcherPath , isRecursive ) ;
426+ }
427+ }
428+ else {
429+ fileWatcherCallbacks . set ( filePath , newCallbacks ) ;
430+ }
410431 }
411432 }
412433
@@ -419,12 +440,14 @@ namespace ts {
419440 // Directory callbacks are not set for file content changes, they are more often used for
420441 // adding/removing/renaming files, which corresponds to the "rename" event
421442 if ( eventName === "rename" && dirWatcherCallbacks . contains ( baseDirPath ) ) {
422- const dirCallback = dirWatcherCallbacks . get ( baseDirPath ) ;
423- dirCallback ( filePath ) ;
443+ for ( const dirCallback of dirWatcherCallbacks . get ( baseDirPath ) ) {
444+ dirCallback ( filePath ) ;
445+ }
424446 }
425447 if ( fileWatcherCallbacks . contains ( filePath ) ) {
426- const fileCallback = fileWatcherCallbacks . get ( filePath ) ;
427- fileCallback ( filePath ) ;
448+ for ( const fileCallback of fileWatcherCallbacks . get ( filePath ) ) {
449+ fileCallback ( filePath ) ;
450+ }
428451 }
429452 }
430453 }
0 commit comments