@@ -61,6 +61,7 @@ class DirectoryWatcher extends EventEmitter {
6161 this . watcherManager = watcherManager ;
6262 this . options = options ;
6363 this . path = directoryPath ;
64+ this . watchingSymlink = false ;
6465 // safeTime is the point in time after which reading is safe to be unchanged
6566 // timestamp is a value that should be compared with another timestamp (mtime)
6667 /** @type {Map<string, { safeTime: number, timestamp: number } } */
@@ -111,8 +112,8 @@ class DirectoryWatcher extends EventEmitter {
111112 this . watchInParentDirectory ( ) ;
112113 }
113114 this . watcher = watchEventSource . watch ( this . path ) ;
114- this . watcher . on ( "change" , this . onWatchEvent . bind ( this ) ) ;
115115 this . watcher . on ( "error" , this . onWatcherError . bind ( this ) ) ;
116+ this . watcher . on ( "change" , this . onWatchEvent . bind ( this ) ) ;
116117 }
117118 } catch ( err ) {
118119 this . onWatcherError ( err ) ;
@@ -550,7 +551,9 @@ class DirectoryWatcher extends EventEmitter {
550551 fs . readdir ( this . path , ( err , items ) => {
551552 if ( this . closed ) return ;
552553 if ( err ) {
553- if ( err . code === "ENOENT" || err . code === "EPERM" ) {
554+ if ( this . watchingSymlink && err . code == "ENOTDIR" ) {
555+ return ;
556+ } else if ( err . code === "ENOENT" || err . code === "EPERM" ) {
554557 this . onDirectoryRemoved ( "scan readdir failed" ) ;
555558 } else {
556559 this . onScanError ( err ) ;
@@ -627,22 +630,7 @@ class DirectoryWatcher extends EventEmitter {
627630 }
628631 } ) ;
629632 for ( const itemPath of itemPaths ) {
630- const handleStats = ( err2 , stats ) => {
631- if ( this . closed ) return ;
632- if ( err2 ) {
633- if (
634- err2 . code === "ENOENT" ||
635- err2 . code === "EPERM" ||
636- err2 . code === "EACCES" ||
637- err2 . code === "EBUSY"
638- ) {
639- this . setMissing ( itemPath , initial , "scan (" + err2 . code + ")" ) ;
640- } else {
641- this . onScanError ( err2 ) ;
642- }
643- itemFinished ( ) ;
644- return ;
645- }
633+ const handleStats = ( stats , symlinkStats ) => {
646634 if ( stats . isFile ( ) || stats . isSymbolicLink ( ) ) {
647635 if ( stats . mtime ) {
648636 ensureFsAccuracy ( stats . mtime ) ;
@@ -654,7 +642,11 @@ class DirectoryWatcher extends EventEmitter {
654642 true ,
655643 "scan (file)"
656644 ) ;
657- } else if ( stats . isDirectory ( ) ) {
645+ }
646+ if (
647+ stats . isDirectory ( ) ||
648+ ( symlinkStats && symlinkStats . isDirectory ( ) )
649+ ) {
658650 if ( ! initial || ! this . directories . has ( itemPath ) )
659651 this . setDirectory (
660652 itemPath ,
@@ -665,11 +657,42 @@ class DirectoryWatcher extends EventEmitter {
665657 }
666658 itemFinished ( ) ;
667659 } ;
668- if ( this . watcherManager . options . followSymlinks ) {
669- fs . stat ( itemPath , handleStats ) ;
670- } else {
671- fs . lstat ( itemPath , handleStats ) ;
672- }
660+ fs . lstat ( itemPath , ( err2 , stats ) => {
661+ if ( this . closed ) return ;
662+ if ( err2 ) {
663+ if (
664+ err2 . code === "ENOENT" ||
665+ err2 . code === "EPERM" ||
666+ err2 . code === "EACCES" ||
667+ err2 . code === "EBUSY"
668+ ) {
669+ this . setMissing ( itemPath , initial , "scan (" + err2 . code + ")" ) ;
670+ } else {
671+ this . onScanError ( err2 ) ;
672+ }
673+ itemFinished ( ) ;
674+ return ;
675+ }
676+ if (
677+ stats . isSymbolicLink ( ) &&
678+ this . watcherManager . options . followSymlinks
679+ ) {
680+ fs . stat ( itemPath , ( err3 , symlinkStats ) => {
681+ if ( this . closed ) return ;
682+ // something is wrong with the symlink, but not with the file itself
683+ if ( err3 ) {
684+ handleStats ( stats ) ;
685+ this . watchingSymlink = false ;
686+ return ;
687+ }
688+ this . watchingSymlink = true ;
689+ handleStats ( stats , symlinkStats ) ;
690+ } ) ;
691+ } else {
692+ this . watchingSymlink = false ;
693+ handleStats ( stats ) ;
694+ }
695+ } ) ;
673696 }
674697 itemFinished ( ) ;
675698 } ) ;
0 commit comments