@@ -230,7 +230,7 @@ function optsGenerator<TOpts>(defaults: TOpts): (opts) => TOpts {
230
230
return options => getOptions ( defaults , options ) ;
231
231
}
232
232
233
- type AssertCallback < T > = T extends Function ? T : never ;
233
+ type AssertCallback < T > = T extends ( ) => void ? T : never ;
234
234
235
235
function validateCallback < T > ( callback : T ) : AssertCallback < T > {
236
236
if ( typeof callback !== 'function' ) throw TypeError ( ERRSTR . CB ) ;
@@ -2688,6 +2688,9 @@ export class FSWatcher extends EventEmitter {
2688
2688
2689
2689
_timer ; // Timer that keeps this task persistent.
2690
2690
2691
+ // inode -> removers
2692
+ private _listenerRemovers = new Map < number , Array < ( ) => void > > ( ) ;
2693
+
2691
2694
constructor ( vol : Volume ) {
2692
2695
super ( ) ;
2693
2696
this . _vol = vol ;
@@ -2711,10 +2714,6 @@ export class FSWatcher extends EventEmitter {
2711
2714
return this . _steps [ this . _steps . length - 1 ] ;
2712
2715
}
2713
2716
2714
- private _onNodeChange = ( ) => {
2715
- this . _emit ( 'change' ) ;
2716
- } ;
2717
-
2718
2717
private _onParentChild = ( link : Link ) => {
2719
2718
if ( link . getName ( ) === this . _getName ( ) ) {
2720
2719
this . _emit ( 'rename' ) ;
@@ -2751,10 +2750,79 @@ export class FSWatcher extends EventEmitter {
2751
2750
throw error ;
2752
2751
}
2753
2752
2754
- this . _link . getNode ( ) . on ( 'change' , this . _onNodeChange ) ;
2753
+ const watchLinkNodeChanged = ( link : Link ) => {
2754
+ const filepath = link . getPath ( ) ;
2755
+ const node = link . getNode ( ) ;
2756
+ const onNodeChange = ( ) => this . emit ( 'change' , 'change' , relative ( this . _filename , filepath ) ) ;
2757
+ node . on ( 'change' , onNodeChange ) ;
2758
+
2759
+ const removers = this . _listenerRemovers . get ( node . ino ) ?? [ ] ;
2760
+ removers . push ( ( ) => node . removeListener ( 'change' , onNodeChange ) ) ;
2761
+ this . _listenerRemovers . set ( node . ino , removers ) ;
2762
+ } ;
2763
+
2764
+ const watchLinkChildrenChanged = ( link : Link ) => {
2765
+ const node = link . getNode ( ) ;
2766
+
2767
+ // when a new link added
2768
+ const onLinkChildAdd = ( l : Link ) => {
2769
+ this . emit ( 'change' , 'rename' , relative ( this . _filename , l . getPath ( ) ) ) ;
2755
2770
2756
- this . _link . on ( 'child:add' , this . _onNodeChange ) ;
2757
- this . _link . on ( 'child:delete' , this . _onNodeChange ) ;
2771
+ setTimeout ( ( ) => {
2772
+ // 1. watch changes of the new link-node
2773
+ watchLinkNodeChanged ( l ) ;
2774
+ // 2. watch changes of the new link-node's children
2775
+ watchLinkChildrenChanged ( l ) ;
2776
+ } ) ;
2777
+ } ;
2778
+
2779
+ // when a new link deleted
2780
+ const onLinkChildDelete = ( l : Link ) => {
2781
+ // remove the listeners of the children nodes
2782
+ const removeLinkNodeListeners = ( curLink : Link ) => {
2783
+ const ino = curLink . getNode ( ) . ino ;
2784
+ const removers = this . _listenerRemovers . get ( ino ) ;
2785
+ if ( removers ) {
2786
+ removers . forEach ( r => r ( ) ) ;
2787
+ this . _listenerRemovers . delete ( ino ) ;
2788
+ }
2789
+ Object . values ( curLink . children ) . forEach ( childLink => {
2790
+ if ( childLink ) {
2791
+ removeLinkNodeListeners ( childLink ) ;
2792
+ }
2793
+ } ) ;
2794
+ } ;
2795
+ removeLinkNodeListeners ( l ) ;
2796
+
2797
+ this . emit ( 'change' , 'rename' , relative ( this . _filename , l . getPath ( ) ) ) ;
2798
+ } ;
2799
+
2800
+ // children nodes changed
2801
+ Object . values ( link . children ) . forEach ( childLink => {
2802
+ if ( childLink ) {
2803
+ watchLinkNodeChanged ( childLink ) ;
2804
+ }
2805
+ } ) ;
2806
+ // link children add/remove
2807
+ link . on ( 'child:add' , onLinkChildAdd ) ;
2808
+ link . on ( 'child:delete' , onLinkChildDelete ) ;
2809
+
2810
+ const removers = this . _listenerRemovers . get ( node . ino ) ?? [ ] ;
2811
+ removers . push ( ( ) => {
2812
+ link . removeListener ( 'child:add' , onLinkChildAdd ) ;
2813
+ link . removeListener ( 'child:delete' , onLinkChildDelete ) ;
2814
+ } ) ;
2815
+
2816
+ if ( recursive ) {
2817
+ Object . values ( link . children ) . forEach ( childLink => {
2818
+ if ( childLink ) {
2819
+ watchLinkChildrenChanged ( childLink ) ;
2820
+ }
2821
+ } ) ;
2822
+ }
2823
+ } ;
2824
+ watchLinkNodeChanged ( this . _link ) ;
2825
+ watchLinkChildrenChanged ( this . _link ) ;
2758
2826
2759
2827
const parent = this . _link . parent ;
2760
2828
if ( parent ) {
@@ -2769,7 +2837,10 @@ export class FSWatcher extends EventEmitter {
2769
2837
close ( ) {
2770
2838
clearTimeout ( this . _timer ) ;
2771
2839
2772
- this . _link . getNode ( ) . removeListener ( 'change' , this . _onNodeChange ) ;
2840
+ this . _listenerRemovers . forEach ( removers => {
2841
+ removers . forEach ( r => r ( ) ) ;
2842
+ } ) ;
2843
+ this . _listenerRemovers . clear ( ) ;
2773
2844
2774
2845
const parent = this . _link . parent ;
2775
2846
if ( parent ) {
0 commit comments