1
+ import path from "path" ;
1
2
import { incrementalVerifier } from "../../../harness/incrementalUtils.js" ;
2
3
import { patchServiceForStateBaseline } from "../../../harness/projectServiceStateLogger.js" ;
3
4
import {
@@ -67,6 +68,130 @@ export function patchHostTimeouts(
67
68
}
68
69
}
69
70
71
+ function patchSessionToHandleWatchEvents ( session : TestSession ) {
72
+ const event = session . event ;
73
+ const idToClose = new Map < number , ( ) => void > ( ) ;
74
+ session . event = ( data , eventName ) => {
75
+ event . call ( session , data , eventName ) ;
76
+ switch ( eventName ) {
77
+ case ts . server . CreateFileWatcherEvent :
78
+ watchFile ( data as ts . server . protocol . CreateFileWatcherEventBody ) ;
79
+ break ;
80
+ case ts . server . CreateDirectoryWatcherEvent :
81
+ watchDirectory ( data as ts . server . protocol . CreateDirectoryWatcherEventBody ) ;
82
+ break ;
83
+ case ts . server . CloseFileWatcherEvent :
84
+ closeWatcher ( data as ts . server . protocol . CloseFileWatcherEventBody ) ;
85
+ break ;
86
+ default :
87
+ break ;
88
+ }
89
+ } ;
90
+
91
+ function watchFile ( event : ts . server . protocol . CreateFileWatcherEventBody ) {
92
+ createWatcher (
93
+ "watchFile" ,
94
+ event ,
95
+ recordChange =>
96
+ session . host . watchUtils . pollingWatch (
97
+ session . host . toNormalizedAbsolutePath ( event . path ) ,
98
+ {
99
+ cb : ( fileName , eventKind ) =>
100
+ recordChange (
101
+ event . id ,
102
+ session . host . windowsStyleRoot ?
103
+ path . win32 . resolve ( fileName ) :
104
+ path . posix . resolve ( fileName ) ,
105
+ eventKind === ts . FileWatcherEventKind . Created ?
106
+ "created" :
107
+ eventKind === ts . FileWatcherEventKind . Deleted ? "deleted" : "updated" ,
108
+ /*ignoreUpdate*/ false ,
109
+ ) ,
110
+ pollingInterval : undefined ! ,
111
+ event,
112
+ } ,
113
+ ) ,
114
+ ) ;
115
+ }
116
+
117
+ function watchDirectory ( event : ts . server . protocol . CreateDirectoryWatcherEventBody ) {
118
+ createWatcher (
119
+ "watchDirectory" ,
120
+ event ,
121
+ recordChange =>
122
+ session . host . watchUtils . fsWatch (
123
+ session . host . toNormalizedAbsolutePath ( event . path ) ,
124
+ event . recursive ,
125
+ {
126
+ cb : ( eventName , relativeFileName ) => {
127
+ if ( ! relativeFileName ) return ;
128
+ const fileName = session . host . windowsStyleRoot ?
129
+ path . win32 . join ( event . path , relativeFileName ) :
130
+ path . posix . join ( event . path , relativeFileName ) ;
131
+ if ( eventName === "change" ) {
132
+ recordChange (
133
+ event . id ,
134
+ fileName ,
135
+ "updated" ,
136
+ ! ! event . ignoreUpdate ,
137
+ ) ;
138
+ }
139
+ else {
140
+ recordChange (
141
+ event . id ,
142
+ fileName ,
143
+ session . host . fileExists ( fileName ) || session . host . directoryExists ( fileName ) ?
144
+ "created" :
145
+ "deleted" ,
146
+ ! ! event . ignoreUpdate ,
147
+ ) ;
148
+ }
149
+ } ,
150
+ inode : undefined ,
151
+ event,
152
+ } ,
153
+ ) ,
154
+ ) ;
155
+ }
156
+
157
+ function createWatcher (
158
+ watchType : string ,
159
+ event : ts . server . protocol . CreateFileWatcherEventBody | ts . server . protocol . CreateDirectoryWatcherEventBody ,
160
+ create : (
161
+ recordChange : (
162
+ id : number ,
163
+ file : string ,
164
+ eventType : "created" | "deleted" | "updated" ,
165
+ ignoreUpdate : boolean ,
166
+ ) => void ,
167
+ ) => ts . FileWatcher ,
168
+ ) {
169
+ session . logger . log ( `Custom ${ watchType } :: Added:: ${ JSON . stringify ( event ) } ` ) ;
170
+ ts . Debug . assert ( ! idToClose . has ( event . id ) ) ;
171
+ const result = create ( ( id , file , eventType , ignoreUpdate ) => {
172
+ const ignored = eventType === "updated" && ignoreUpdate ;
173
+ session . logger . log ( `Custom ${ watchType } :: Triggered${ ignoreUpdate ? " Ignored" : "" } :: ${ JSON . stringify ( event ) } :: ${ file } ${ eventType } ` ) ;
174
+ if ( ! ignored ) {
175
+ let watchChange = session . watchChanges . get ( id ) ;
176
+ if ( ! watchChange ) session . watchChanges . set ( id , watchChange = { id } ) ;
177
+ ( watchChange [ eventType ] ??= [ ] ) . push ( file ) ;
178
+ }
179
+ } ) ;
180
+ idToClose . set ( event . id , ( ) => {
181
+ session . logger . log ( `Custom ${ watchType } :: Close:: ${ JSON . stringify ( event ) } ` ) ;
182
+ result . close ( ) ;
183
+ } ) ;
184
+ }
185
+
186
+ function closeWatcher ( data : ts . server . protocol . CloseFileWatcherEventBody ) {
187
+ const close = idToClose . get ( data . id ) ;
188
+ if ( close ) {
189
+ idToClose . delete ( data . id ) ;
190
+ close ( ) ;
191
+ }
192
+ }
193
+ }
194
+
70
195
export interface TestSessionOptions extends ts . server . SessionOptions , TestTypingsInstallerOptions {
71
196
host : TestServerHost ;
72
197
logger : LoggerWithInMemoryLogs ;
@@ -90,6 +215,7 @@ export class TestSession extends ts.server.Session {
90
215
public override logger ! : LoggerWithInMemoryLogs ;
91
216
public override readonly typingsInstaller ! : TestTypingsInstallerAdapter ;
92
217
public serverCancellationToken : TestServerCancellationToken ;
218
+ public watchChanges = new Map < number , ts . server . protocol . WatchChangeRequestArgs > ( ) ;
93
219
94
220
constructor ( optsOrHost : TestSessionConstructorOptions ) {
95
221
const opts = getTestSessionPartialOptionsAndHost ( optsOrHost ) ;
@@ -125,6 +251,7 @@ export class TestSession extends ts.server.Session {
125
251
if ( opts . regionDiagLineCountThreshold !== undefined ) {
126
252
this . regionDiagLineCountThreshold = opts . regionDiagLineCountThreshold ;
127
253
}
254
+ if ( opts . canUseWatchEvents ) patchSessionToHandleWatchEvents ( this ) ;
128
255
}
129
256
130
257
getProjectService ( ) {
@@ -159,6 +286,15 @@ export class TestSession extends ts.server.Session {
159
286
request . type = "request" ;
160
287
return this . executeCommand ( request ) ;
161
288
}
289
+
290
+ public invokeWatchChanges ( ) {
291
+ const changes = ts . singleOrMany ( ts . arrayFrom ( this . watchChanges . values ( ) ) ) ;
292
+ this . watchChanges . clear ( ) ;
293
+ this . executeCommandSeq < ts . server . protocol . WatchChangeRequest > ( {
294
+ command : ts . server . protocol . CommandTypes . WatchChange ,
295
+ arguments : changes ,
296
+ } ) ;
297
+ }
162
298
}
163
299
164
300
export function createSessionWithCustomEventHandler (
0 commit comments