@@ -35,6 +35,7 @@ namespace ts {
3535 }
3636
3737 export interface DirectoryWatcher extends FileWatcher {
38+ directoryPath : Path ;
3839 referenceCount : number ;
3940 }
4041
@@ -306,123 +307,71 @@ namespace ts {
306307
307308 function createWatchedFileSet ( ) {
308309 const dirWatchers = createFileMap < DirectoryWatcher > ( ) ;
309- const recursiveDirWatchers = createFileMap < DirectoryWatcher > ( ) ;
310310 // One file can have multiple watchers
311311 const fileWatcherCallbacks = createFileMap < FileWatcherCallback [ ] > ( ) ;
312- const dirWatcherCallbacks = createFileMap < DirectoryWatcherCallback [ ] > ( ) ;
313-
314312 const currentDirectory = process . cwd ( ) ;
315- return { addFile, removeFile, addDir } ;
313+ return { addFile, removeFile } ;
316314
317- function addDir ( dirName : string , callback : DirectoryWatcherCallback , recursive ?: boolean ) {
318- const dirPath = toPath ( dirName , currentDirectory , getCanonicalPath ) ;
319- if ( ! dirWatcherCallbacks . contains ( dirPath ) ) {
320- dirWatcherCallbacks . set ( dirPath , [ callback ] ) ;
321- }
322- else {
323- dirWatcherCallbacks . get ( dirPath ) . push ( callback ) ;
324- }
325- const { watcher, isRecursive } = addDirWatcher ( dirPath , recursive ) ;
326- return {
327- close : ( ) => reduceDirWatcherRefCount ( watcher , dirPath , isRecursive )
328- } ;
329- }
330-
331- function reduceDirWatcherRefCount ( watcher : DirectoryWatcher , dirPath : Path , isRecursive : boolean ) {
315+ function reduceDirWatcherRefCount ( dirPath : Path ) {
316+ const watcher = dirWatchers . get ( dirPath ) ;
332317 watcher . referenceCount -= 1 ;
333318 if ( watcher . referenceCount <= 0 ) {
334319 watcher . close ( ) ;
335- if ( isRecursive ) {
336- recursiveDirWatchers . remove ( dirPath ) ;
337- }
338- else {
339- dirWatchers . remove ( dirPath ) ;
340- }
320+ dirWatchers . remove ( dirPath ) ;
341321 }
342322 }
343323
344- function addDirWatcher ( dirPath : Path , recursive ?: boolean ) : { watcher : DirectoryWatcher , isRecursive : boolean } {
345- let watchers : FileMap < DirectoryWatcher > ;
346- const options : { persistent : boolean , recursive ?: boolean } = { persistent : true } ;
324+ function addDirWatcher ( dirPath : Path ) : void {
325+ if ( dirWatchers . contains ( dirPath ) ) {
326+ const watcher = dirWatchers . get ( dirPath ) ;
327+ watcher . referenceCount += 1 ;
328+ return ;
329+ }
330+
331+ const watcher : DirectoryWatcher = _fs . watch (
332+ dirPath ,
333+ { persistent : true } ,
334+ ( eventName : string , relativeFileName : string ) => fileEventHandler ( eventName , relativeFileName , dirPath )
335+ ) ;
336+ watcher . referenceCount = 1 ;
337+ dirWatchers . set ( dirPath , watcher ) ;
338+ return ;
339+ }
347340
348- // Node 4.0 `fs.watch` function supports the "recursive" option on both OSX and Windows
349- // (ref: https://github.com/nodejs/node/pull/2649 and https://github.com/Microsoft/TypeScript/issues/4643)
350- if ( isNode4OrLater ( ) && recursive === true &&
351- ( process . platform === "win32" || process . platform === "darwin" ) ) {
352- if ( recursiveDirWatchers . contains ( dirPath ) ) {
353- const watcher = recursiveDirWatchers . get ( dirPath ) ;
354- watcher . referenceCount += 1 ;
355- return { watcher, isRecursive : true } ;
356- }
357- watchers = recursiveDirWatchers ;
358- options . recursive = true ;
341+ function addFileWatcherCallback ( filePath : Path , callback : FileWatcherCallback ) : void {
342+ if ( fileWatcherCallbacks . contains ( filePath ) ) {
343+ fileWatcherCallbacks . get ( filePath ) . push ( callback ) ;
359344 }
360345 else {
361- if ( dirWatchers . contains ( dirPath ) ) {
362- const watcher = dirWatchers . get ( dirPath ) ;
363- watcher . referenceCount += 1 ;
364- return { watcher, isRecursive : false } ;
365- }
366- watchers = dirWatchers ;
367- options . recursive = false ;
346+ fileWatcherCallbacks . set ( filePath , [ callback ] ) ;
368347 }
369-
370- const watcher : DirectoryWatcher = _fs . watch ( dirPath , options , ( eventName : string , relativeFileName : string ) => fileEventHandler ( eventName , relativeFileName , dirPath ) ) ;
371- watcher . referenceCount = 1 ;
372- watchers . set ( dirPath , watcher ) ;
373- return { watcher, isRecursive : options . recursive } ;
374348 }
375349
376- function findDirWatcherForFile ( filePath : Path ) : { watcher : DirectoryWatcher , watcherPath : Path , isRecursive : boolean } {
377- let watcher : DirectoryWatcher ;
378- let watcherPath : Path ;
379- let isRecursive = false ;
380- recursiveDirWatchers . forEachValue ( dirPath => {
381- if ( filePath . indexOf ( dirPath ) === 0 ) {
382- watcherPath = dirPath ;
383- watcher = recursiveDirWatchers . get ( dirPath ) ;
384- isRecursive = true ;
385- return ;
386- }
387- } ) ;
388- if ( ! watcher ) {
389- const parentDirPath = getDirectoryPath ( filePath ) ;
390- if ( dirWatchers . contains ( parentDirPath ) ) {
391- watcherPath = parentDirPath ;
392- watcher = dirWatchers . get ( parentDirPath ) ;
393- }
350+ function findWatchedDirForFile ( filePath : Path ) : Path {
351+ const dirPath = getDirectoryPath ( filePath ) ;
352+ if ( dirWatchers . contains ( dirPath ) ) {
353+ return dirPath ;
394354 }
395- return { watcher , watcherPath , isRecursive } ;
355+ return undefined ;
396356 }
397357
398358 function addFile ( fileName : string , callback : FileWatcherCallback ) : WatchedFile {
399359 const filePath = toPath ( fileName , currentDirectory , getCanonicalPath ) ;
360+ addFileWatcherCallback ( filePath , callback ) ;
361+ addDirWatcher ( getDirectoryPath ( filePath ) ) ;
400362
401- if ( fileWatcherCallbacks . contains ( filePath ) ) {
402- fileWatcherCallbacks . get ( filePath ) . push ( callback ) ;
403- }
404- else {
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 ] ) ;
413- }
414363 return { fileName, callback } ;
415364 }
416365
417- function removeFile ( file : WatchedFile ) {
418- const filePath = toPath ( file . fileName , currentDirectory , getCanonicalPath ) ;
366+ function removeFile ( watchedFile : WatchedFile ) {
367+ const filePath = toPath ( watchedFile . fileName , currentDirectory , getCanonicalPath ) ;
419368 if ( fileWatcherCallbacks . contains ( filePath ) ) {
420- const newCallbacks = copyListRemovingItem ( file . callback , fileWatcherCallbacks . get ( filePath ) ) ;
369+ const newCallbacks = copyListRemovingItem ( watchedFile . callback , fileWatcherCallbacks . get ( filePath ) ) ;
421370 if ( newCallbacks . length === 0 ) {
422371 fileWatcherCallbacks . remove ( filePath ) ;
423- const { watcher , watcherPath , isRecursive } = findDirWatcherForFile ( filePath ) ;
424- if ( watcher ) {
425- reduceDirWatcherRefCount ( watcher , watcherPath , isRecursive ) ;
372+ const watchedDir = findWatchedDirForFile ( filePath ) ;
373+ if ( watchedDir ) {
374+ reduceDirWatcherRefCount ( watchedDir ) ;
426375 }
427376 }
428377 else {
@@ -437,14 +386,7 @@ namespace ts {
437386 function fileEventHandler ( eventName : string , relativefileName : string , baseDirPath : Path ) {
438387 // When files are deleted from disk, the triggered "rename" event would have a relativefileName of "undefined"
439388 const filePath = relativefileName === undefined ? undefined : toPath ( relativefileName , baseDirPath , getCanonicalPath ) ;
440- // Directory callbacks are not set for file content changes, they are more often used for
441- // adding/removing/renaming files, which corresponds to the "rename" event
442- if ( eventName === "rename" && dirWatcherCallbacks . contains ( baseDirPath ) ) {
443- for ( const dirCallback of dirWatcherCallbacks . get ( baseDirPath ) ) {
444- dirCallback ( filePath ) ;
445- }
446- }
447- if ( fileWatcherCallbacks . contains ( filePath ) ) {
389+ if ( eventName === "change" && fileWatcherCallbacks . contains ( filePath ) ) {
448390 for ( const fileCallback of fileWatcherCallbacks . get ( filePath ) ) {
449391 fileCallback ( filePath ) ;
450392 }
@@ -577,7 +519,29 @@ namespace ts {
577519 } ;
578520 } ,
579521 watchDirectory : ( path , callback , recursive ) => {
580- return watchedFileSet . addDir ( path , callback , recursive ) ;
522+ // Node 4.0 `fs.watch` function supports the "recursive" option on both OSX and Windows
523+ // (ref: https://github.com/nodejs/node/pull/2649 and https://github.com/Microsoft/TypeScript/issues/4643)
524+ let options : any ;
525+ if ( isNode4OrLater ( ) && ( process . platform === "win32" || process . platform === "darwin" ) ) {
526+ options = { persistent : true , recursive : ! ! recursive } ;
527+ }
528+ else {
529+ options = { persistent : true } ;
530+ }
531+
532+ return _fs . watch (
533+ path ,
534+ options ,
535+ ( eventName : string , relativeFileName : string ) => {
536+ // In watchDirectory we only care about adding and removing files (when event name is
537+ // "rename"); changes made within files are handled by corresponding fileWatchers (when
538+ // event name is "change")
539+ if ( eventName === "rename" ) {
540+ // When deleting a file, the passed baseFileName is null
541+ callback ( ! relativeFileName ? relativeFileName : normalizePath ( combinePaths ( path , relativeFileName ) ) ) ;
542+ } ;
543+ }
544+ ) ;
581545 } ,
582546 resolvePath : function ( path : string ) : string {
583547 return _path . resolve ( path ) ;
@@ -658,5 +622,3 @@ namespace ts {
658622 }
659623 } ) ( ) ;
660624}
661-
662-
0 commit comments