@@ -731,88 +731,107 @@ function doWatchNonRecursive(file: { path: string, isDirectory: boolean }, onCha
731731 // File path: use path directly for files and join with changed file name otherwise
732732 const changedFilePath = file . isDirectory ? join ( file . path , changedFileName ) : file . path ;
733733
734- // File: rename/delete
735- if ( ! file . isDirectory && ( type === 'rename' || changedFileName !== originalFileName ) ) {
736- // The file was either deleted or renamed. Many tools apply changes to files in an
737- // atomic way ("Atomic Save") by first renaming the file to a temporary name and then
738- // renaming it back to the original name. Our watcher will detect this as a rename
739- // and then stops to work on Mac and Linux because the watcher is applied to the
740- // inode and not the name. The fix is to detect this case and trying to watch the file
741- // again after a certain delay.
742- // In addition, we send out a delete event if after a timeout we detect that the file
743- // does indeed not exist anymore.
744-
745- // Wait a bit and try to install watcher again, assuming that the file was renamed quickly ("Atomic Save")
746- const timeoutHandle = setTimeout ( async ( ) => {
747- const fileExists = await exists ( changedFilePath ) ;
748-
749- if ( disposed ) {
750- return ; // ignore if disposed by now
751- }
734+ // File
735+ if ( ! file . isDirectory ) {
736+ if ( type === 'rename' || changedFileName !== originalFileName ) {
737+ // The file was either deleted or renamed. Many tools apply changes to files in an
738+ // atomic way ("Atomic Save") by first renaming the file to a temporary name and then
739+ // renaming it back to the original name. Our watcher will detect this as a rename
740+ // and then stops to work on Mac and Linux because the watcher is applied to the
741+ // inode and not the name. The fix is to detect this case and trying to watch the file
742+ // again after a certain delay.
743+ // In addition, we send out a delete event if after a timeout we detect that the file
744+ // does indeed not exist anymore.
745+
746+ // Wait a bit and try to install watcher again, assuming that the file was renamed quickly ("Atomic Save")
747+ const timeoutHandle = setTimeout ( async ( ) => {
748+ const fileExists = await exists ( changedFilePath ) ;
749+
750+ if ( disposed ) {
751+ return ; // ignore if disposed by now
752+ }
752753
753- // File still exists, so emit as change event and reapply the watcher
754- if ( fileExists ) {
755- onChange ( 'changed' , changedFilePath ) ;
754+ // File still exists, so emit as change event and reapply the watcher
755+ if ( fileExists ) {
756+ onChange ( 'changed' , changedFilePath ) ;
756757
757- watcherDisposables = [ doWatchNonRecursive ( file , onChange , onError ) ] ;
758- }
758+ watcherDisposables = [ doWatchNonRecursive ( file , onChange , onError ) ] ;
759+ }
759760
760- // File seems to be really gone, so emit a deleted event
761- else {
762- onChange ( 'deleted' , changedFilePath ) ;
763- }
764- } , 300 ) ;
761+ // File seems to be really gone, so emit a deleted event
762+ else {
763+ onChange ( 'deleted' , changedFilePath ) ;
764+ }
765+ } , 300 ) ;
765766
766- // Very important to dispose the watcher which now points to a stale inode
767- // and wire in a new disposable that tracks our timeout that is installed
768- dispose ( watcherDisposables ) ;
769- watcherDisposables = [ toDisposable ( ( ) => clearTimeout ( timeoutHandle ) ) ] ;
767+ // Very important to dispose the watcher which now points to a stale inode
768+ // and wire in a new disposable that tracks our timeout that is installed
769+ dispose ( watcherDisposables ) ;
770+ watcherDisposables = [ toDisposable ( ( ) => clearTimeout ( timeoutHandle ) ) ] ;
771+ } else {
772+ onChange ( 'changed' , changedFilePath ) ;
773+ }
770774 }
771775
772- // Folder: add/delete
773- else if ( file . isDirectory && type === 'rename' ) {
776+ // Folder
777+ else {
774778
775- // Cancel any previous stats for this file path if existing
776- const statDisposable = mapPathToStatDisposable . get ( changedFilePath ) ;
777- if ( statDisposable ) {
778- dispose ( statDisposable ) ;
779- }
779+ // Children add/delete
780+ if ( type === 'rename' ) {
780781
781- // Wait a bit and try see if the file still exists on disk to decide on the resulting event
782- const timeoutHandle = setTimeout ( async ( ) => {
783- mapPathToStatDisposable . delete ( changedFilePath ) ;
782+ // Cancel any previous stats for this file path if existing
783+ const statDisposable = mapPathToStatDisposable . get ( changedFilePath ) ;
784+ if ( statDisposable ) {
785+ dispose ( statDisposable ) ;
786+ }
784787
785- const fileExists = await exists ( changedFilePath ) ;
788+ // Wait a bit and try see if the file still exists on disk to decide on the resulting event
789+ const timeoutHandle = setTimeout ( async ( ) => {
790+ mapPathToStatDisposable . delete ( changedFilePath ) ;
786791
787- if ( disposed ) {
788- return ; // ignore if disposed by now
789- }
792+ const fileExists = await exists ( changedFilePath ) ;
790793
791- // Figure out the correct event type:
792- // File Exists: either 'added' or 'changed' if known before
793- // File Does not Exist: always 'deleted'
794- let type : 'added' | 'deleted' | 'changed' ;
795- if ( fileExists ) {
796- if ( folderChildren . has ( changedFileName ) ) {
797- type = 'changed' ;
794+ if ( disposed ) {
795+ return ; // ignore if disposed by now
796+ }
797+
798+ // Figure out the correct event type:
799+ // File Exists: either 'added' or 'changed' if known before
800+ // File Does not Exist: always 'deleted'
801+ let type : 'added' | 'deleted' | 'changed' ;
802+ if ( fileExists ) {
803+ if ( folderChildren . has ( changedFileName ) ) {
804+ type = 'changed' ;
805+ } else {
806+ type = 'added' ;
807+ folderChildren . add ( changedFileName ) ;
808+ }
798809 } else {
799- type = 'added' ;
800- folderChildren . add ( changedFileName ) ;
810+ folderChildren . delete ( changedFileName ) ;
811+ type = 'deleted' ;
801812 }
813+
814+ onChange ( type , changedFilePath ) ;
815+ } , 100 ) ;
816+
817+ mapPathToStatDisposable . set ( changedFilePath , toDisposable ( ( ) => clearTimeout ( timeoutHandle ) ) ) ;
818+ }
819+
820+ // Other events
821+ else {
822+
823+ // Figure out the correct event type: if this is the
824+ // first time we see this child, it can only be added
825+ let type : 'added' | 'changed' ;
826+ if ( folderChildren . has ( changedFileName ) ) {
827+ type = 'changed' ;
802828 } else {
803- folderChildren . delete ( changedFileName ) ;
804- type = 'deleted' ;
829+ type = 'added' ;
830+ folderChildren . add ( changedFileName ) ;
805831 }
806832
807833 onChange ( type , changedFilePath ) ;
808- } , 100 ) ;
809-
810- mapPathToStatDisposable . set ( changedFilePath , toDisposable ( ( ) => clearTimeout ( timeoutHandle ) ) ) ;
811- }
812-
813- // Other events
814- else {
815- onChange ( 'changed' , changedFilePath ) ;
834+ }
816835 }
817836 } ) ;
818837 } catch ( error ) {
0 commit comments