Skip to main content
interlace
Plugin: modernizationRules

prefer-event-target

Prefer EventTarget over EventEmitter for isomorphic code

Prefer EventTarget over EventEmitter for isomorphic code. This rule is part of eslint-plugin-modernization.

Quick Summary

AspectDetails
SeverityWarning (compatibility)
Auto-Fix❌ No (different API)
CategoryModernization
ESLint MCP✅ Optimized for ESLint MCP integration
Best ForIsomorphic code, browser compatibility

Rule Details

EventTarget is available in both browsers and Node.js (v14.5+), while EventEmitter is Node.js-specific.

The rule reports Node's EventEmitter, found by where the binding comes from (events / node:events). A named or renamed import and a require destructure (plain or quoted key) are reported where they are bound. The default import (the module's default export is EventEmitter) and the required module itself (const E = require('events')) are reported where they are used, at new E() or class Bus extends E, because the same binding also reaches other events exports such as E.once. A namespace member (ev.EventEmitter) and require('events').EventEmitter are reported where they are read. require must be Node's loader, either the global or one made by createRequire.

It does not report an EventEmitter from another module (Angular's @angular/core EventEmitter is an RxJS Subject), a class the file declares itself, a member of a parameter (events.EventEmitter where events is passed in), or other events exports such as once. A named binding (import { EventEmitter }, const { EventEmitter } = require('events')) is reported once, where it is bound. An undeclared EventEmitter or events.EventEmitter is still reported by name.

Configuration

OptionTypeDefaultDescription
allowEventEmitterbooleanfalseTurn the rule off

Why This Matters

IssueImpactSolution
🌐 Browser supportEventEmitter not in browsersUse EventTarget
📦 Bundle sizePolyfills neededNative API
🔄 Isomorphic codeDifferent APIs per environmentStandardize on EventTarget

Examples

❌ Incorrect

import { EventEmitter } from 'events';

class MyEmitter extends EventEmitter {
  emit(event: string) {
    super.emit(event);
  }
}

✅ Correct

class MyEmitter extends EventTarget {
  dispatch(type: string, detail?: unknown) {
    this.dispatchEvent(new CustomEvent(type, { detail }));
  }
}

// Usage
const emitter = new MyEmitter();
emitter.addEventListener('change', (e) => console.log(e));
emitter.dispatch('change', { value: 42 });

Configuration Examples

Basic Usage

{
  rules: {
    'modernization/prefer-event-target': 'warn'
  }
}

Further Reading

Known False Negatives

The following patterns are not detected due to static analysis limitations:

Dynamic Variable References

Why: Static analysis cannot trace values stored in variables or passed through function parameters.

// ❌ NOT DETECTED - Value from variable
const value = externalSource();
processValue(value); // Variable origin not tracked

Mitigation: Implement runtime validation and review code manually. Consider using TypeScript branded types for validated inputs.

Imported Values

Why: When values come from imports, the rule cannot analyze their origin or construction.

// ❌ NOT DETECTED - Value from import
import { getValue } from './helpers';
processValue(getValue()); // Cross-file not tracked

Mitigation: Ensure imported values follow the same constraints. Use TypeScript for type safety.

Use this rule

npm install -D oxlint-plugin-modernization
.oxlintrc.json
{  "jsPlugins": ["oxlint-plugin-modernization"],  "rules": {    "modernization/prefer-event-target": "error"  }}
vite.config.ts
import { defineConfig } from 'vite-plus';export default defineConfig({  lint: {    jsPlugins: ['oxlint-plugin-modernization'],    rules: {      'modernization/prefer-event-target': 'error',    },  },});

oxlint-plugin-modernization re-exports eslint-plugin-modernization/oxlint: the same rules under the same IDs, and no ESLint install. Why oxlint.

Did this rule catch something? Star the repo to get new CWE coverage as we ship it — or follow the AI-code-security benchmarks behind these rules.