Repository: denoland/deno
Pull request: fix(ext/node): support 2nd arg of `EventEmitter.prototype.listenerCount`

This PR adds the support of the 2nd arg of `EventEmitter.prototype.listenerCount` API. If you pass listener as the 2nd arg, it only counts the matching listeners.

This enables `parallel/test-events-listener-count-with-listener.js`.

towards #29595 

Changed files:
filename: ext/node/polyfills/_events.mjs
patch: @@ -739,17 +739,36 @@ EventEmitter.prototype.rawListeners = function rawListeners(type) {
  * Returns the number of listeners listening to event name
  * specified as `type`.
  * @param {string | symbol} type
+ * @param {Function} listener
  * @returns {number}
  */
-const _listenerCount = function listenerCount(type) {
+const _listenerCount = function listenerCount(type, listener) {
   const events = this._events;
 
   if (events !== undefined) {
     const evlistener = events[type];
 
     if (typeof evlistener === "function") {
+      if (listener != null) {
+        return listener === evlistener || listener === evlistener.listener
+          ? 1
+          : 0;
+      }
       return 1;
     } else if (evlistener !== undefined) {
+      if (listener != null) {
+        let matching = 0;
+
+        for (let i = 0, l = evlistener.length; i < l; i++) {
+          if (
+            evlistener[i] === listener || evlistener[i].listener === listener
+          ) {
+            matching++;
+          }
+        }
+
+        return matching;
+      }
       return evlistener.length;
     }
   }

filename: tests/node_compat/config.jsonc
patch: @@ -538,6 +538,7 @@
       "test-events-add-abort-listener.mjs",
       "test-events-getmaxlisteners.js",
       "test-events-list.js",
+      "test-events-listener-count-with-listener.js",
       "test-events-on-async-iterator.js",
       "test-events-once.js",
       "test-events-uncaught-exception-stack.js",

filename: tests/node_compat/runner/TODO.md
patch: @@ -1,7 +1,7 @@
 <!-- deno-fmt-ignore-file -->
 # Remaining Node Tests
 
-1200 tests out of 3993 have been ported from Node 23.9.0 (30.05% ported, 70.55% remaining).
+1201 tests out of 3993 have been ported from Node 23.9.0 (30.08% ported, 70.52% remaining).
 
 NOTE: This file should not be manually edited. Please edit `tests/node_compat/config.json` and run `deno task setup` in `tests/node_compat/runner` dir instead.
 
@@ -744,7 +744,6 @@ NOTE: This file should not be manually edited. Please edit `tests/node_compat/co
 - [parallel/test-eval-disallow-code-generation-from-strings.js](https://github.com/nodejs/node/tree/v23.9.0/test/parallel/test-eval-disallow-code-generation-from-strings.js)
 - [parallel/test-eventemitter-asyncresource.js](https://github.com/nodejs/node/tree/v23.9.0/test/parallel/test-eventemitter-asyncresource.js)
 - [parallel/test-events-customevent.js](https://github.com/nodejs/node/tree/v23.9.0/test/parallel/test-events-customevent.js)
-- [parallel/test-events-listener-count-with-listener.js](https://github.com/nodejs/node/tree/v23.9.0/test/parallel/test-events-listener-count-with-listener.js)
 - [parallel/test-events-static-geteventlisteners.js](https://github.com/nodejs/node/tree/v23.9.0/test/parallel/test-events-static-geteventlisteners.js)
 - [parallel/test-eventsource-disabled.js](https://github.com/nodejs/node/tree/v23.9.0/test/parallel/test-eventsource-disabled.js)
 - [parallel/test-eventsource.js](https://github.com/nodejs/node/tree/v23.9.0/test/parallel/test-eventsource.js)

filename: tests/node_compat/test/parallel/test-events-listener-count-with-listener.js
patch: @@ -0,0 +1,72 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 23.9.0
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+'use strict';
+
+const common = require('../common');
+const EventEmitter = require('events');
+const assert = require('assert');
+
+const EE = new EventEmitter();
+const handler = common.mustCall(undefined, 3);
+const anotherHandler = common.mustCall();
+
+assert.strictEqual(EE.listenerCount('event'), 0);
+assert.strictEqual(EE.listenerCount('event', handler), 0);
+assert.strictEqual(EE.listenerCount('event', anotherHandler), 0);
+
+EE.once('event', handler);
+
+assert.strictEqual(EE.listenerCount('event'), 1);
+assert.strictEqual(EE.listenerCount('event', handler), 1);
+assert.strictEqual(EE.listenerCount('event', anotherHandler), 0);
+
+EE.removeAllListeners('event');
+
+assert.strictEqual(EE.listenerCount('event'), 0);
+assert.strictEqual(EE.listenerCount('event', handler), 0);
+assert.strictEqual(EE.listenerCount('event', anotherHandler), 0);
+
+EE.on('event', handler);
+
+assert.strictEqual(EE.listenerCount('event'), 1);
+assert.strictEqual(EE.listenerCount('event', handler), 1);
+assert.strictEqual(EE.listenerCount('event', anotherHandler), 0);
+
+EE.once('event', anotherHandler);
+
+assert.strictEqual(EE.listenerCount('event'), 2);
+assert.strictEqual(EE.listenerCount('event', handler), 1);
+assert.strictEqual(EE.listenerCount('event', anotherHandler), 1);
+
+assert.strictEqual(EE.listenerCount('another-event'), 0);
+assert.strictEqual(EE.listenerCount('another-event', handler), 0);
+assert.strictEqual(EE.listenerCount('another-event', anotherHandler), 0);
+
+EE.once('event', handler);
+
+assert.strictEqual(EE.listenerCount('event'), 3);
+assert.strictEqual(EE.listenerCount('event', handler), 2);
+assert.strictEqual(EE.listenerCount('event', anotherHandler), 1);
+
+EE.emit('event');
+
+assert.strictEqual(EE.listenerCount('event'), 1);
+assert.strictEqual(EE.listenerCount('event', handler), 1);
+assert.strictEqual(EE.listenerCount('event', anotherHandler), 0);
+
+EE.emit('event');
+
+assert.strictEqual(EE.listenerCount('event'), 1);
+assert.strictEqual(EE.listenerCount('event', handler), 1);
+assert.strictEqual(EE.listenerCount('event', anotherHandler), 0);
+
+EE.off('event', handler);
+
+assert.strictEqual(EE.listenerCount('event'), 0);
+assert.strictEqual(EE.listenerCount('event', handler), 0);
+assert.strictEqual(EE.listenerCount('event', anotherHandler), 0);