@@ -87,7 +87,13 @@ export namespace Emitter {
87
87
Target extends Emitter < any > ,
88
88
Type extends keyof EventMap & string ,
89
89
EventMap extends DefaultEventMap = InferEventMap < Target > ,
90
- > = DataToEvent < Type , EventMap [ Type ] [ 0 ] >
90
+ > = DataToEvent < Type , Emitter . EventDataType < Target , Type , EventMap > >
91
+
92
+ export type EventDataType <
93
+ Target extends Emitter < any > ,
94
+ Type extends keyof EventMap & string ,
95
+ EventMap extends DefaultEventMap = InferEventMap < Target > ,
96
+ > = EventMap [ Type ] [ 0 ]
91
97
}
92
98
93
99
export class Emitter < EventMap extends DefaultEventMap = { } > {
@@ -196,66 +202,124 @@ export class Emitter<EventMap extends DefaultEventMap = {}> {
196
202
* @returns {boolean } Returns `true` if the event had any listeners, `false` otherwise.
197
203
*/
198
204
public emit < Type extends keyof EventMap & string > (
199
- ...args : EventMap [ Type ] [ 0 ] extends [ never ]
205
+ event : Emitter . EventType < typeof this , Type , EventMap > ,
206
+ ) : boolean
207
+ public emit < Type extends keyof EventMap & string > (
208
+ ...args : Emitter . EventDataType < typeof this , Type , EventMap > extends [ never ]
200
209
? [ type : Type ]
201
- : [ type : Type , data : EventMap [ Type ] [ 0 ] ]
210
+ : [ type : Type , data : Emitter . EventDataType < typeof this , Type , EventMap > ]
211
+ ) : boolean
212
+
213
+ public emit < Type extends keyof EventMap & string > (
214
+ ...args :
215
+ | [ Emitter . EventType < typeof this , Type , EventMap > ]
216
+ | ( Emitter . EventDataType < typeof this , Type , EventMap > extends [ never ]
217
+ ? [ type : Type ]
218
+ : [
219
+ type : Type ,
220
+ data : Emitter . EventDataType < typeof this , Type , EventMap > ,
221
+ ] )
202
222
) : boolean {
203
- if ( ! this . #listeners[ args [ 0 ] ] || this . #listeners[ args [ 0 ] ] . length === 0 ) {
223
+ const [ eventOrType , data ] = args
224
+ const isEvent = eventOrType instanceof Event
225
+ const type = isEvent ? eventOrType . type : eventOrType
226
+
227
+ if ( ! this . #listeners[ type ] || this . #listeners[ type ] . length === 0 ) {
204
228
return false
205
229
}
206
230
207
- const event = this . #createEventForData( args [ 0 ] , args [ 1 ] )
231
+ const event = isEvent
232
+ ? eventOrType
233
+ : this . createEvent . call ( this , type , data )
208
234
209
- for ( const listener of this . #listeners[ args [ 0 ] ] ) {
210
- if ( this . #listenerOptions. get ( listener ) . signal . aborted ) {
211
- continue
235
+ for ( const listener of this . #listeners[ type ] ) {
236
+ if (
237
+ event [ kPropagationStopped ] != null &&
238
+ event [ kPropagationStopped ] !== this
239
+ ) {
240
+ return false
212
241
}
213
242
214
243
if ( event [ kImmediatePropagationStopped ] ) {
215
244
break
216
245
}
217
246
247
+ if ( this . #listenerOptions. get ( listener ) . signal . aborted ) {
248
+ continue
249
+ }
250
+
218
251
this . #callListener( listener , event )
219
252
}
220
253
221
- this . #eventsCache. delete ( [ args [ 0 ] , args [ 1 ] ] )
254
+ this . #eventsCache. delete ( [ type , data ] )
222
255
223
256
return true
224
257
}
225
258
226
259
/**
227
- * Emits the given event and returns a Promise that resolves
228
- * with the array of listener results, or rejects as soon as any
229
- * of the listeners throw. Listeners are still called synchronously
230
- * to guarantee call order and prevent race conditions.
260
+ * Emits the given event and returns a Promise that always resolves
261
+ * with the array of listener results (either fulfilled or rejected).
262
+ * The listeners are still called synchronously to guarantee call order
263
+ * and prevent race conditions.
231
264
*/
232
- public async emitAsPromise < Type extends keyof EventMap & string > (
233
- ...args : EventMap [ Type ] [ 0 ] extends [ never ]
265
+ public emitAsPromise < Type extends keyof EventMap & string > (
266
+ event : Emitter . EventType < typeof this , Type , EventMap > ,
267
+ ) : Promise < Array < Emitter . ListenerReturnType < typeof this , Type , EventMap > > >
268
+ public emitAsPromise < Type extends keyof EventMap & string > (
269
+ ...args : Emitter . EventDataType < typeof this , Type , EventMap > extends [ never ]
234
270
? [ type : Type ]
235
- : [ type : Type , data : EventMap [ Type ] [ 0 ] ]
271
+ : [ type : Type , data : Emitter . EventDataType < typeof this , Type , EventMap > ]
272
+ ) : Promise < Array < Emitter . ListenerReturnType < typeof this , Type , EventMap > > >
273
+
274
+ public async emitAsPromise < Type extends keyof EventMap & string > (
275
+ ...args :
276
+ | [ Emitter . EventType < typeof this , Type , EventMap > ]
277
+ | ( Emitter . EventDataType < typeof this , Type , EventMap > extends [ never ]
278
+ ? [ type : Type ]
279
+ : [
280
+ type : Type ,
281
+ data : Emitter . EventDataType < typeof this , Type , EventMap > ,
282
+ ] )
236
283
) : Promise < Array < Emitter . ListenerReturnType < typeof this , Type , EventMap > > > {
237
- if ( ! this . #listeners[ args [ 0 ] ] || this . #listeners[ args [ 0 ] ] . length === 0 ) {
284
+ const [ eventOrType , data ] = args
285
+ const isEvent = eventOrType instanceof Event
286
+ const type = isEvent ? eventOrType . type : eventOrType
287
+
288
+ if ( ! this . #listeners[ type ] || this . #listeners[ type ] . length === 0 ) {
238
289
return [ ]
239
290
}
240
291
241
- const event = this . #createEventForData( args [ 0 ] , args [ 1 ] )
242
- const pendingListeners : Array < Promise < unknown > > = [ ]
292
+ const event = isEvent
293
+ ? eventOrType
294
+ : this . createEvent . call ( this , type , data )
243
295
244
- for ( const listener of this . #listeners[ args [ 0 ] ] ) {
245
- if ( this . #listenerOptions. get ( listener ) . signal . aborted ) {
246
- continue
296
+ const pendingListeners : Array <
297
+ Promise < Emitter . ListenerReturnType < typeof this , Type , EventMap > >
298
+ > = [ ]
299
+
300
+ for ( const listener of this . #listeners[ type ] ) {
301
+ if (
302
+ event [ kPropagationStopped ] != null &&
303
+ event [ kPropagationStopped ] !== this
304
+ ) {
305
+ return [ ]
247
306
}
248
307
249
308
if ( event [ kImmediatePropagationStopped ] ) {
250
309
break
251
310
}
252
311
312
+ if ( this . #listenerOptions. get ( listener ) . signal . aborted ) {
313
+ continue
314
+ }
315
+
253
316
pendingListeners . push (
317
+ // Awaiting individual listeners guarantees their call order.
254
318
await Promise . resolve ( this . #callListener( listener , event ) ) ,
255
319
)
256
320
}
257
321
258
- this . #eventsCache. delete ( [ args [ 0 ] , args [ 1 ] ] )
322
+ this . #eventsCache. delete ( [ type , data ] )
259
323
260
324
return Promise . allSettled ( pendingListeners ) . then ( ( results ) => {
261
325
return results . map ( ( result ) =>
@@ -266,33 +330,60 @@ export class Emitter<EventMap extends DefaultEventMap = {}> {
266
330
267
331
/**
268
332
* Emits the given event and returns a generator that yields
269
- * the result of each listener. This way, you stop exhausting
333
+ * the result of each listener in order . This way, you stop exhausting
270
334
* the listeners once you get the expected value.
271
335
*/
272
- public * emitAsGenerator < Type extends keyof EventMap & string > (
273
- ...args : EventMap [ Type ] [ 0 ] extends [ never ]
336
+ public emitAsGenerator < Type extends keyof EventMap & string > (
337
+ event : Emitter . EventType < typeof this , Type , EventMap > ,
338
+ ) : Generator < Emitter . ListenerReturnType < typeof this , Type , EventMap > >
339
+ public emitAsGenerator < Type extends keyof EventMap & string > (
340
+ ...args : Emitter . EventDataType < typeof this , Type , EventMap > extends [ never ]
274
341
? [ type : Type ]
275
- : [ type : Type , data : EventMap [ Type ] [ 0 ] ]
276
- ) : Generator < unknown > {
277
- if ( ! this . #listeners[ args [ 0 ] ] || this . #listeners[ args [ 0 ] ] . length === 0 ) {
342
+ : [ type : Type , data : Emitter . EventDataType < typeof this , Type , EventMap > ]
343
+ ) : Generator < Emitter . ListenerReturnType < typeof this , Type , EventMap > >
344
+
345
+ public * emitAsGenerator < Type extends keyof EventMap & string > (
346
+ ...args :
347
+ | [ event : Emitter . EventType < typeof this , Type , EventMap > ]
348
+ | ( Emitter . EventDataType < typeof this , Type , EventMap > extends [ never ]
349
+ ? [ type : Type ]
350
+ : [
351
+ type : Type ,
352
+ data : Emitter . EventDataType < typeof this , Type , EventMap > ,
353
+ ] )
354
+ ) : Generator < Emitter . ListenerReturnType < typeof this , Type , EventMap > > {
355
+ const [ eventOrType , data ] = args
356
+ const isEvent = eventOrType instanceof Event
357
+ const type = isEvent ? eventOrType . type : eventOrType
358
+
359
+ if ( ! this . #listeners[ type ] || this . #listeners[ type ] . length === 0 ) {
278
360
return
279
361
}
280
362
281
- const event = this . #createEventForData( args [ 0 ] , args [ 1 ] )
363
+ const event = isEvent
364
+ ? eventOrType
365
+ : this . createEvent . call ( this , type , data )
282
366
283
- for ( const listener of this . #listeners[ args [ 0 ] ] ) {
284
- if ( this . #listenerOptions. get ( listener ) . signal . aborted ) {
285
- continue
367
+ for ( const listener of this . #listeners[ type ] ) {
368
+ if (
369
+ event [ kPropagationStopped ] != null &&
370
+ event [ kPropagationStopped ] !== this
371
+ ) {
372
+ return
286
373
}
287
374
288
375
if ( event [ kImmediatePropagationStopped ] ) {
289
376
break
290
377
}
291
378
379
+ if ( this . #listenerOptions. get ( listener ) . signal . aborted ) {
380
+ continue
381
+ }
382
+
292
383
yield this . #callListener( listener , event )
293
384
}
294
385
295
- this . #eventsCache. delete ( [ args [ 0 ] , args [ 1 ] ] )
386
+ this . #eventsCache. delete ( [ type , data ] )
296
387
}
297
388
298
389
/**
@@ -377,27 +468,61 @@ export class Emitter<EventMap extends DefaultEventMap = {}> {
377
468
this . #listeners[ type ] . push ( listener )
378
469
}
379
470
380
- #createEventForData< Type extends keyof EventMap & string > (
381
- type : Type ,
382
- data : EventMap [ Type ] [ 0 ] ,
383
- ) : Event {
471
+ public createEvent <
472
+ Type extends keyof EventMap & string ,
473
+ EmitterEvent extends Emitter . EventType <
474
+ typeof this ,
475
+ Type ,
476
+ EventMap
477
+ > = Emitter . EventType < typeof this , Type , EventMap > ,
478
+ > (
479
+ ...args : Emitter . EventDataType < typeof this , Type , EventMap > extends [ never ]
480
+ ? [ type : Type ]
481
+ : [ type : Type , data : Emitter . EventDataType < typeof this , Type , EventMap > ]
482
+ ) : EmitterEvent {
483
+ const [ type , data ] = args
384
484
const cachedEvent = this . #eventsCache. get ( [ type , data ] )
385
485
386
486
if ( cachedEvent ) {
387
- return cachedEvent
487
+ return cachedEvent as EmitterEvent
388
488
}
389
489
390
490
let event =
391
491
data == null
392
- ? new Event ( type , { cancelable : true } )
393
- : new MessageEvent ( type , { data, cancelable : true } )
492
+ ? ( new Event ( type , { cancelable : true } ) as EmitterEvent )
493
+ : ( new MessageEvent ( type , { data, cancelable : true } ) as EmitterEvent )
394
494
395
495
Object . defineProperties ( event , {
496
+ defaultPrevented : {
497
+ enumerable : false ,
498
+ writable : true ,
499
+ value : false ,
500
+ } ,
501
+ preventDefault : {
502
+ enumerable : false ,
503
+ value : new Proxy ( event . preventDefault , {
504
+ apply : ( target , thisArg , argArray ) => {
505
+ /**
506
+ * @note Node.js 18 does NOT update the `defaultPrevented` value
507
+ * when you call `preventDefault()`. This is a bug in Node.js.
508
+ *
509
+ * @fixme Remove this hack when Node.js 20 is the minimal version.
510
+ */
511
+ Reflect . set ( event , 'defaultPrevented' , true )
512
+ return Reflect . apply ( target , thisArg , argArray )
513
+ } ,
514
+ } ) ,
515
+ } ,
396
516
stopPropagation : {
397
517
enumerable : false ,
398
518
value : new Proxy ( event . stopPropagation , {
399
- apply ( target , thisArg , argArray ) {
400
- event [ kPropagationStopped ] = true
519
+ apply : ( target , thisArg , argArray ) => {
520
+ /**
521
+ * @note Propagation is also stopped when the immediate propagation is stopped.
522
+ * Because of that, store the reference to the Emitter instance that stopped it.
523
+ * (Node.js makes `thisArg` to be the `Event` that stops it).
524
+ */
525
+ event [ kPropagationStopped ] = this
401
526
return Reflect . apply ( target , thisArg , argArray )
402
527
} ,
403
528
} ) ,
0 commit comments