diff --git a/src/js/node/events.ts b/src/js/node/events.ts index f769584658..ca9cce2d3b 100644 --- a/src/js/node/events.ts +++ b/src/js/node/events.ts @@ -438,12 +438,20 @@ function getEventListeners(emitter, type) { return getEventListenersForEventTarget(emitter, type); } -function setMaxListeners(n, ...eventTargets) { +// https://github.com/nodejs/node/blob/2eff28fb7a93d3f672f80b582f664a7c701569fb/lib/events.js#L315-L339 +function setMaxListeners(n = defaultMaxListeners, ...eventTargets) { validateNumber(n, "setMaxListeners", 0); - var length; - if (eventTargets && (length = eventTargets.length)) { - for (let i = 0; i < length; i++) { - eventTargets[i].setMaxListeners(n); + const length = eventTargets?.length; + if (length) { + for (let eventTargetOrEmitter of eventTargets) { + // TODO: EventTarget setMaxListeners is not implemented yet. + // Only EventEmitter has it. + if ($isCallable(eventTargetOrEmitter?.setMaxListeners)) { + eventTargetOrEmitter.setMaxListeners(n); + } else if ($isObject(eventTargetOrEmitter) && eventTargetOrEmitter instanceof EventTarget) { + // This is a fake number so that the number can be checked against with getMaxListeners() + eventTargetOrEmitter[eventTargetMaxListenersSymbol] = n; + } } } else { defaultMaxListeners = n; @@ -527,9 +535,9 @@ function validateBoolean(value, name) { let AsyncResource = null; +const eventTargetMaxListenersSymbol = Symbol("EventTarget.maxListeners"); function getMaxListeners(emitterOrTarget) { - // TODO: apparently EventTarget in Node can have a max number of listeners? - return emitterOrTarget?._maxListeners ?? defaultMaxListeners; + return emitterOrTarget?.[eventTargetMaxListenersSymbol] ?? emitterOrTarget?._maxListeners ?? defaultMaxListeners; } // Copy-pasta from Node.js source code diff --git a/test/js/node/events/event-emitter.test.ts b/test/js/node/events/event-emitter.test.ts index 2c52001fc3..626222387c 100644 --- a/test/js/node/events/event-emitter.test.ts +++ b/test/js/node/events/event-emitter.test.ts @@ -4,7 +4,7 @@ import { createRequire } from "module"; // this is also testing that imports with default and named imports in the same statement work // our transpiler transform changes this to a var with import.meta.require -import EventEmitter, { getEventListeners, captureRejectionSymbol } from "node:events"; +import EventEmitter, { getEventListeners, captureRejectionSymbol, setMaxListeners, getMaxListeners } from "node:events"; describe("node:events", () => { test("captureRejectionSymbol", () => { @@ -853,6 +853,23 @@ test("getMaxListeners", () => { expect(emitter.getMaxListeners()).toBe(20); }); +test("setMaxListeners", () => { + const emitter = new EventEmitter(); + expect(emitter.getMaxListeners()).toBe(10); + emitter.setMaxListeners(20); + expect(emitter.getMaxListeners()).toBe(20); + + setMaxListeners(30, emitter); + expect(emitter.getMaxListeners()).toBe(30); + + const eventTarget = new EventTarget(); + setMaxListeners(1, eventTarget); + expect(getMaxListeners(eventTarget)).toBe(1); + + setMaxListeners(99, eventTarget); + expect(getMaxListeners(eventTarget)).toBe(99); +}); + test("getEventListeners", () => { const target = new EventTarget(); expect(getEventListeners(target, "hey").length).toBe(0);