diff --git a/lib/internal/event_target.js b/lib/internal/event_target.js index 99988a3dcd6fc5..a82d9e7a4e6cf9 100644 --- a/lib/internal/event_target.js +++ b/lib/internal/event_target.js @@ -171,6 +171,14 @@ Object.defineProperty(Event.prototype, SymbolToStringTag, { value: 'Event', }); +class NodeCustomEvent extends Event { + constructor(type, options) { + super(type, options); + if (options && options.detail) { + this.detail = options.detail; + } + } +} // The listeners for an EventTarget are maintained as a linked list. // Unfortunately, the way EventTarget is defined, listeners are accounted // using the tuple [handler,capture], and even if we don't actually make @@ -376,6 +384,9 @@ class EventTarget { event[kTarget] = undefined; } + [kCreateEvent](nodeValue, type) { + return new NodeCustomEvent(type, { detail: nodeValue }); + } [customInspectSymbol](depth, options) { const name = this.constructor.name; if (depth < 0) @@ -473,6 +484,14 @@ class NodeEventTarget extends EventTarget { this.addEventListener(type, listener, { [kIsNodeStyleListener]: true }); return this; } + emit(type, arg) { + if (typeof type !== 'string') { + throw new ERR_INVALID_ARG_TYPE('type', 'string', type); + } + const hadListeners = this.listenerCount(type) > 0; + this[kHybridDispatch](arg, type); + return hadListeners; + } once(type, listener) { this.addEventListener(type, listener, @@ -501,6 +520,7 @@ Object.defineProperties(NodeEventTarget.prototype, { on: { enumerable: true }, addListener: { enumerable: true }, once: { enumerable: true }, + emit: { enumerable: true }, removeAllListeners: { enumerable: true }, }); diff --git a/lib/internal/worker/io.js b/lib/internal/worker/io.js index 5b5118a1d70b21..319283852ce2ea 100644 --- a/lib/internal/worker/io.js +++ b/lib/internal/worker/io.js @@ -30,6 +30,7 @@ const { const { Readable, Writable } = require('stream'); const { Event, + EventTarget, NodeEventTarget, defineEventHandler, initNodeEventTarget, @@ -79,11 +80,15 @@ class MessageEvent extends Event { } } +const originalCreateEvent = EventTarget.prototype[kCreateEvent]; ObjectDefineProperty( MessagePort.prototype, kCreateEvent, { value: function(data, type) { + if (type !== 'message' && type !== 'messageerror') { + return originalCreateEvent.call(this, data, type); + } return new MessageEvent(data, this, type); }, configurable: false, diff --git a/test/parallel/test-nodeeventtarget.js b/test/parallel/test-nodeeventtarget.js index 0f9218f540860d..276bd9feb4da7c 100644 --- a/test/parallel/test-nodeeventtarget.js +++ b/test/parallel/test-nodeeventtarget.js @@ -11,6 +11,7 @@ const { deepStrictEqual, ok, strictEqual, + throws, } = require('assert'); const { on } = require('events'); @@ -145,6 +146,27 @@ const { on } = require('events'); target.on('foo', () => {}); target.on('foo', () => {}); } +{ + // Test NodeEventTarget emit + const emitter = new NodeEventTarget(); + emitter.addEventListener('foo', common.mustCall((e) => { + strictEqual(e.type, 'foo'); + strictEqual(e.detail, 'bar'); + ok(e instanceof Event); + }), { once: true }); + emitter.once('foo', common.mustCall((e, droppedAdditionalArgument) => { + strictEqual(e, 'bar'); + strictEqual(droppedAdditionalArgument, undefined); + })); + emitter.emit('foo', 'bar', 'baz'); +} +{ + // Test NodeEventTarget emit unsupported usage + const emitter = new NodeEventTarget(); + throws(() => { + emitter.emit(); + }, /ERR_INVALID_ARG_TYPE/); +} (async () => { // test NodeEventTarget async-iterability diff --git a/test/parallel/test-worker-message-port.js b/test/parallel/test-worker-message-port.js index 4f4863c45ed516..6f4e09d029d168 100644 --- a/test/parallel/test-worker-message-port.js +++ b/test/parallel/test-worker-message-port.js @@ -16,7 +16,18 @@ const { MessageChannel, MessagePort } = require('worker_threads'); port2.close(common.mustCall()); })); } - +{ + // Test emitting non-message events on a port + const { port2 } = new MessageChannel(); + port2.addEventListener('foo', common.mustCall((received) => { + assert.strictEqual(received.type, 'foo'); + assert.strictEqual(received.detail, 'bar'); + })); + port2.on('foo', common.mustCall((received) => { + assert.strictEqual(received, 'bar'); + })); + port2.emit('foo', 'bar'); +} { const { port1, port2 } = new MessageChannel();