1
1
import {
2
- addRange ,
3
2
arrayFrom ,
4
3
compareStringsCaseSensitive ,
5
4
contains ,
@@ -10,6 +9,7 @@ import {
10
9
GetCanonicalFileName ,
11
10
MultiMap ,
12
11
PollingInterval ,
12
+ System ,
13
13
} from "./_namespaces/ts" ;
14
14
15
15
export interface TestFileWatcher {
@@ -25,7 +25,7 @@ export interface TestFsWatcher<DirCallback> {
25
25
export interface Watches < Data > {
26
26
add ( path : string , data : Data ) : void ;
27
27
remove ( path : string , data : Data ) : void ;
28
- forEach ( path : string , cb : ( data : Data ) => void ) : void ;
28
+ forEach ( path : string , cb : ( data : Data , path : string ) => void ) : void ;
29
29
serialize ( baseline : string [ ] ) : void ;
30
30
}
31
31
@@ -44,6 +44,7 @@ export function createWatchUtils<PollingWatcherData, FsWatcherData>(
44
44
pollingWatchesName : string ,
45
45
fsWatchesName : string ,
46
46
getCanonicalFileName : GetCanonicalFileName ,
47
+ system : Required < Pick < System , "realpath" > > ,
47
48
) : WatchUtils < PollingWatcherData , FsWatcherData > {
48
49
const pollingWatches = initializeWatches < PollingWatcherData > ( pollingWatchesName ) ;
49
50
const fsWatches = initializeWatches < FsWatcherData > ( fsWatchesName ) ;
@@ -64,6 +65,8 @@ export function createWatchUtils<PollingWatcherData, FsWatcherData>(
64
65
const actuals = createMultiMap < string , Data > ( ) ;
65
66
let serialized : Map < string , Data [ ] > | undefined ;
66
67
let canonicalPathsToStrings : Map < string , Set < string > > | undefined ;
68
+ let realToLinked : MultiMap < string , string > | undefined ;
69
+ let pathToReal : Map < string , string > | undefined ;
67
70
return {
68
71
add,
69
72
remove,
@@ -73,40 +76,69 @@ export function createWatchUtils<PollingWatcherData, FsWatcherData>(
73
76
74
77
function add ( path : string , data : Data ) {
75
78
actuals . add ( path , data ) ;
76
- if ( actuals . get ( path ) ! . length === 1 ) {
77
- const canonicalPath = getCanonicalFileName ( path ) ;
78
- if ( canonicalPath !== path ) {
79
- ( canonicalPathsToStrings ??= new Map ( ) ) . set (
80
- canonicalPath ,
81
- ( canonicalPathsToStrings ?. get ( canonicalPath ) ?? new Set ( ) ) . add ( path ) ,
82
- ) ;
83
- }
79
+ if ( actuals . get ( path ) ! . length !== 1 ) return ;
80
+ const canonicalPath = getCanonicalFileName ( path ) ;
81
+ if ( canonicalPath !== path ) {
82
+ ( canonicalPathsToStrings ??= new Map ( ) ) . set (
83
+ canonicalPath ,
84
+ ( canonicalPathsToStrings ?. get ( canonicalPath ) ?? new Set ( ) ) . add ( path ) ,
85
+ ) ;
86
+ }
87
+ const real = system . realpath ( path ) ;
88
+ ( pathToReal ??= new Map ( ) ) . set ( path , real ) ;
89
+ if ( real === path ) return ;
90
+ const canonicalReal = getCanonicalFileName ( real ) ;
91
+ if ( getCanonicalFileName ( path ) !== canonicalReal ) {
92
+ ( realToLinked ??= createMultiMap ( ) ) . add ( canonicalReal , path ) ;
84
93
}
85
94
}
86
95
87
96
function remove ( path : string , data : Data ) {
88
97
actuals . remove ( path , data ) ;
89
- if ( ! actuals . has ( path ) ) {
90
- const canonicalPath = getCanonicalFileName ( path ) ;
91
- if ( canonicalPath !== path ) {
92
- const existing = canonicalPathsToStrings ! . get ( canonicalPath ) ;
93
- if ( existing ! . size === 1 ) canonicalPathsToStrings ! . delete ( canonicalPath ) ;
94
- else existing ! . delete ( path ) ;
95
- }
98
+ if ( actuals . has ( path ) ) return ;
99
+ const canonicalPath = getCanonicalFileName ( path ) ;
100
+ if ( canonicalPath !== path ) {
101
+ const existing = canonicalPathsToStrings ! . get ( canonicalPath ) ;
102
+ if ( existing ! . size === 1 ) canonicalPathsToStrings ! . delete ( canonicalPath ) ;
103
+ else existing ! . delete ( path ) ;
104
+ }
105
+ const real = pathToReal ?. get ( path ) ! ;
106
+ pathToReal ! . delete ( path ) ;
107
+ if ( real === path ) return ;
108
+ const canonicalReal = getCanonicalFileName ( real ) ;
109
+ if ( getCanonicalFileName ( path ) !== canonicalReal ) {
110
+ realToLinked ! . remove ( canonicalReal , path ) ;
96
111
}
97
112
}
98
113
99
- function forEach ( path : string , cb : ( data : Data ) => void ) {
100
- let allData : Data [ ] | undefined ;
101
- allData = addRange ( allData , actuals . get ( path ) ) ;
114
+ function getAllData ( path : string ) {
115
+ let allData : Map < string , Data [ ] > | undefined ;
116
+ addData ( path ) ;
102
117
const canonicalPath = getCanonicalFileName ( path ) ;
103
- if ( canonicalPath !== path ) allData = addRange ( allData , actuals . get ( canonicalPath ) ) ;
118
+ if ( canonicalPath !== path ) addData ( canonicalPath ) ;
104
119
canonicalPathsToStrings ?. get ( canonicalPath ) ?. forEach ( canonicalSamePath => {
105
120
if ( canonicalSamePath !== path && canonicalSamePath !== canonicalPath ) {
106
- allData = addRange ( allData , actuals . get ( canonicalSamePath ) ) ;
121
+ addData ( canonicalSamePath ) ;
107
122
}
108
123
} ) ;
109
- allData ?. forEach ( cb ) ;
124
+ return allData ;
125
+ function addData ( path : string ) {
126
+ const data = actuals . get ( path ) ;
127
+ if ( data ) ( allData ??= new Map ( ) ) . set ( path , data ) ;
128
+ }
129
+ }
130
+
131
+ function forEach ( path : string , cb : ( data : Data , path : string ) => void ) {
132
+ const real = system . realpath ( path ) ;
133
+ const canonicalPath = getCanonicalFileName ( path ) ;
134
+ const canonicalReal = getCanonicalFileName ( real ) ;
135
+ let allData = canonicalPath === canonicalReal ? getAllData ( path ) : getAllData ( real ) ;
136
+ realToLinked ?. get ( canonicalReal ) ?. forEach ( linked => {
137
+ if ( allData ?. has ( linked ) ) return ;
138
+ const data = actuals . get ( linked ) ;
139
+ if ( data ) ( allData ??= new Map ( ) ) . set ( linked , data ) ;
140
+ } ) ;
141
+ allData ?. forEach ( ( data , path ) => data . forEach ( d => cb ( d , path ) ) ) ;
110
142
}
111
143
112
144
function serialize ( baseline : string [ ] ) {
0 commit comments