@@ -4,27 +4,52 @@ const {
4
4
FunctionPrototypeCall,
5
5
ObjectDefineProperty,
6
6
ObjectSetPrototypeOf,
7
+ Promise,
7
8
Symbol,
8
9
} = primordials ;
9
10
10
- const errors = require ( 'internal/errors' ) ;
11
+ const {
12
+ AbortError,
13
+ uvException,
14
+ codes : {
15
+ ERR_INVALID_ARG_VALUE ,
16
+ } ,
17
+ } = require ( 'internal/errors' ) ;
18
+
11
19
const {
12
20
kFsStatsFieldsNumber,
13
21
StatWatcher : _StatWatcher
14
22
} = internalBinding ( 'fs' ) ;
23
+
15
24
const { FSEvent } = internalBinding ( 'fs_event_wrap' ) ;
16
25
const { UV_ENOSPC } = internalBinding ( 'uv' ) ;
17
26
const { EventEmitter } = require ( 'events' ) ;
27
+
18
28
const {
19
29
getStatsFromBinding,
20
30
getValidatedPath
21
31
} = require ( 'internal/fs/utils' ) ;
32
+
22
33
const {
23
34
defaultTriggerAsyncIdScope,
24
35
symbols : { owner_symbol }
25
36
} = require ( 'internal/async_hooks' ) ;
37
+
26
38
const { toNamespacedPath } = require ( 'path' ) ;
27
- const { validateUint32 } = require ( 'internal/validators' ) ;
39
+
40
+ const {
41
+ validateAbortSignal,
42
+ validateBoolean,
43
+ validateObject,
44
+ validateUint32,
45
+ } = require ( 'internal/validators' ) ;
46
+
47
+ const {
48
+ Buffer : {
49
+ isEncoding,
50
+ } ,
51
+ } = require ( 'buffer' ) ;
52
+
28
53
const assert = require ( 'internal/assert' ) ;
29
54
30
55
const kOldStatus = Symbol ( 'kOldStatus' ) ;
@@ -91,7 +116,7 @@ StatWatcher.prototype[kFSStatWatcherStart] = function(filename,
91
116
validateUint32 ( interval , 'interval' ) ;
92
117
const err = this . _handle . start ( toNamespacedPath ( filename ) , interval ) ;
93
118
if ( err ) {
94
- const error = errors . uvException ( {
119
+ const error = uvException ( {
95
120
errno : err ,
96
121
syscall : 'watch' ,
97
122
path : filename
@@ -176,7 +201,7 @@ function FSWatcher() {
176
201
this . _handle . close ( ) ;
177
202
this . _handle = null ; // Make the handle garbage collectable.
178
203
}
179
- const error = errors . uvException ( {
204
+ const error = uvException ( {
180
205
errno : status ,
181
206
syscall : 'watch' ,
182
207
path : filename
@@ -216,7 +241,7 @@ FSWatcher.prototype[kFSWatchStart] = function(filename,
216
241
recursive ,
217
242
encoding ) ;
218
243
if ( err ) {
219
- const error = errors . uvException ( {
244
+ const error = uvException ( {
220
245
errno : err ,
221
246
syscall : 'watch' ,
222
247
path : filename ,
@@ -270,10 +295,94 @@ ObjectDefineProperty(FSEvent.prototype, 'owner', {
270
295
set ( v ) { return this [ owner_symbol ] = v ; }
271
296
} ) ;
272
297
298
+ async function * watch ( filename , options = { } ) {
299
+ const path = toNamespacedPath ( getValidatedPath ( filename ) ) ;
300
+ validateObject ( options , 'options' ) ;
301
+
302
+ const {
303
+ persistent = true ,
304
+ recursive = false ,
305
+ encoding = 'utf8' ,
306
+ signal,
307
+ } = options ;
308
+
309
+ validateBoolean ( persistent , 'options.persistent' ) ;
310
+ validateBoolean ( recursive , 'options.recursive' ) ;
311
+ validateAbortSignal ( signal , 'options.signal' ) ;
312
+
313
+ if ( encoding && ! isEncoding ( encoding ) ) {
314
+ const reason = 'is invalid encoding' ;
315
+ throw new ERR_INVALID_ARG_VALUE ( encoding , 'encoding' , reason ) ;
316
+ }
317
+
318
+ if ( signal ?. aborted )
319
+ throw new AbortError ( ) ;
320
+
321
+ const handle = new FSEvent ( ) ;
322
+ let res ;
323
+ let rej ;
324
+ const oncancel = ( ) => {
325
+ handle . close ( ) ;
326
+ rej ( new AbortError ( ) ) ;
327
+ } ;
328
+
329
+ try {
330
+ signal ?. addEventListener ( 'abort' , oncancel , { once : true } ) ;
331
+
332
+ let promise = new Promise ( ( resolve , reject ) => {
333
+ res = resolve ;
334
+ rej = reject ;
335
+ } ) ;
336
+
337
+ handle . onchange = ( status , eventType , filename ) => {
338
+ if ( status < 0 ) {
339
+ const error = uvException ( {
340
+ errno : status ,
341
+ syscall : 'watch' ,
342
+ path : filename
343
+ } ) ;
344
+ error . filename = filename ;
345
+ handle . close ( ) ;
346
+ rej ( error ) ;
347
+ return ;
348
+ }
349
+
350
+ res ( { eventType, filename } ) ;
351
+ } ;
352
+
353
+ const err = handle . start ( path , persistent , recursive , encoding ) ;
354
+ if ( err ) {
355
+ const error = uvException ( {
356
+ errno : err ,
357
+ syscall : 'watch' ,
358
+ path : filename ,
359
+ message : err === UV_ENOSPC ?
360
+ 'System limit for number of file watchers reached' : ''
361
+ } ) ;
362
+ error . filename = filename ;
363
+ handle . close ( ) ;
364
+ throw error ;
365
+ }
366
+
367
+ while ( ! signal ?. aborted ) {
368
+ yield await promise ;
369
+ promise = new Promise ( ( resolve , reject ) => {
370
+ res = resolve ;
371
+ rej = reject ;
372
+ } ) ;
373
+ }
374
+ throw new AbortError ( ) ;
375
+ } finally {
376
+ handle . close ( ) ;
377
+ signal ?. removeEventListener ( 'abort' , oncancel ) ;
378
+ }
379
+ }
380
+
273
381
module . exports = {
274
382
FSWatcher,
275
383
StatWatcher,
276
384
kFSWatchStart,
277
385
kFSStatWatcherStart,
278
386
kFSStatWatcherAddOrCleanRef,
387
+ watch,
279
388
} ;
0 commit comments