diff --git a/lib/internal/event_target.js b/lib/internal/event_target.js index 8c3ca67337baea..12e04098320bc3 100644 --- a/lib/internal/event_target.js +++ b/lib/internal/event_target.js @@ -172,6 +172,14 @@ ObjectDefineProperty(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 @@ -377,6 +385,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) @@ -474,6 +485,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, @@ -502,6 +521,7 @@ ObjectDefineProperties(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 e3ca6fe0cc10b5..763d06856cbd42 100644 --- a/lib/internal/worker/io.js +++ b/lib/internal/worker/io.js @@ -127,11 +127,15 @@ ObjectDefineProperties(MessageEvent.prototype, { }, }); +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(type, { data }); }, 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();