Skip to content

Commit 166524a

Browse files
jopemachinefiskersindresorhus
authored
Add prefer-event-target rule (#1792)
Co-authored-by: fisker Cheung <[email protected]> Co-authored-by: Sindre Sorhus <[email protected]>
1 parent 6f5ecf5 commit 166524a

File tree

7 files changed

+194
-0
lines changed

7 files changed

+194
-0
lines changed

configs/recommended.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ module.exports = {
7878
'unicorn/prefer-dom-node-dataset': 'error',
7979
'unicorn/prefer-dom-node-remove': 'error',
8080
'unicorn/prefer-dom-node-text-content': 'error',
81+
'unicorn/prefer-event-target': 'error',
8182
'unicorn/prefer-export-from': 'error',
8283
'unicorn/prefer-includes': 'error',
8384
'unicorn/prefer-json-parse-buffer': 'off',

docs/rules/prefer-event-target.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Prefer `EventTarget` over `EventEmitter`
2+
3+
<!-- Do not manually modify RULE_NOTICE part. Run: `npm run generate-rule-notices` -->
4+
<!-- RULE_NOTICE -->
5+
*This rule is part of the [recommended](https://github.com/sindresorhus/eslint-plugin-unicorn#recommended-config) config.*
6+
<!-- /RULE_NOTICE -->
7+
8+
While [`EventEmitter`](https://nodejs.org/api/events.html#class-eventemitter) is only available in Node.js, [`EventTarget`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget) is also available in *Deno* and browsers.
9+
10+
This rule reduces the bundle size and makes your code more cross-platform friendly.
11+
12+
See the [differences](https://nodejs.org/api/events.html#eventtarget-and-event-api) between `EventEmitter` and `EventTarget`.
13+
14+
## Fail
15+
16+
```js
17+
import {EventEmitter} from 'node:event';
18+
19+
class Foo extends EventEmitter {}
20+
```
21+
22+
```js
23+
const emitter = new EventEmitter();
24+
```
25+
26+
## Pass
27+
28+
```js
29+
class Foo extends EventTarget {}
30+
```
31+
32+
```js
33+
const target = new EventTarget();
34+
```

readme.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ Each rule has emojis denoting:
118118
| [prefer-dom-node-dataset](docs/rules/prefer-dom-node-dataset.md) | Prefer using `.dataset` on DOM elements over calling attribute methods. || 🔧 | |
119119
| [prefer-dom-node-remove](docs/rules/prefer-dom-node-remove.md) | Prefer `childNode.remove()` over `parentNode.removeChild(childNode)`. || 🔧 | 💡 |
120120
| [prefer-dom-node-text-content](docs/rules/prefer-dom-node-text-content.md) | Prefer `.textContent` over `.innerText`. || | 💡 |
121+
| [prefer-event-target](docs/rules/prefer-event-target.md) | Prefer `EventTarget` over `EventEmitter`. || | |
121122
| [prefer-export-from](docs/rules/prefer-export-from.md) | Prefer `export…from` when re-exporting. || 🔧 | 💡 |
122123
| [prefer-includes](docs/rules/prefer-includes.md) | Prefer `.includes()` over `.indexOf()` and `Array#some()` when checking for existence or non-existence. || 🔧 | 💡 |
123124
| [prefer-json-parse-buffer](docs/rules/prefer-json-parse-buffer.md) | Prefer reading a JSON file as a buffer. | | 🔧 | |

rules/prefer-event-target.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
'use strict';
2+
const {matches} = require('./selectors/index.js');
3+
4+
const MESSAGE_ID = 'prefer-event-target';
5+
const messages = {
6+
[MESSAGE_ID]: 'Prefer `EventTarget` over `EventEmitter`.',
7+
};
8+
9+
const selector = [
10+
'Identifier',
11+
'[name="EventEmitter"]',
12+
matches([
13+
'ClassDeclaration > .superClass',
14+
'ClassExpression > .superClass',
15+
'NewExpression > .callee',
16+
]),
17+
].join('');
18+
19+
/** @param {import('eslint').Rule.RuleContext} context */
20+
const create = () => ({
21+
[selector](node) {
22+
return {
23+
node,
24+
messageId: MESSAGE_ID,
25+
};
26+
},
27+
});
28+
29+
/** @type {import('eslint').Rule.RuleModule} */
30+
module.exports = {
31+
create,
32+
meta: {
33+
type: 'suggestion',
34+
docs: {
35+
description: 'Prefer `EventTarget` over `EventEmitter`.',
36+
},
37+
messages,
38+
},
39+
};

test/prefer-event-target.mjs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import outdent from 'outdent';
2+
import {getTester} from './utils/test.mjs';
3+
4+
const {test} = getTester(import.meta);
5+
6+
test.snapshot({
7+
valid: [
8+
'class Foo {}',
9+
'class Foo extends OtherClass {}',
10+
'class Foo extends EventTarget {}',
11+
'const Foo = class extends EventTarget {}',
12+
'const Foo = class extends foo.EventTarget {}',
13+
'const Foo = class extends foo.bar.EventTarget {}',
14+
'class Foo extends foo.EventEmitter {}',
15+
'class Foo extends foo.bar.EventEmitter {}',
16+
'class EventEmitter extends Foo {}',
17+
'const Foo = class EventEmitter extends Foo {}',
18+
'new Foo(EventEmitter)',
19+
'new foo.EventEmitter()',
20+
],
21+
invalid: [
22+
'class Foo extends EventEmitter {}',
23+
'class Foo extends EventEmitter { someMethod() {} }',
24+
'const Foo = class extends EventEmitter {}',
25+
outdent`
26+
class Foo extends EventEmitter {
27+
addListener() {}
28+
removeListener() {}
29+
}
30+
`,
31+
],
32+
});
33+
34+
test.snapshot({
35+
valid: [
36+
'EventTarget()',
37+
'new EventTarget',
38+
'const target = new EventTarget;',
39+
'const target = EventTarget()',
40+
'const target = new Foo(EventEmitter);',
41+
'EventEmitter()',
42+
'const emitter = EventEmitter()',
43+
],
44+
invalid: [
45+
'new EventEmitter',
46+
'const emitter = new EventEmitter;',
47+
],
48+
});
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# Snapshot report for `test/prefer-event-target.mjs`
2+
3+
The actual snapshot is saved in `prefer-event-target.mjs.snap`.
4+
5+
Generated by [AVA](https://avajs.dev).
6+
7+
## Invalid #1
8+
1 | class Foo extends EventEmitter {}
9+
10+
> Error 1/1
11+
12+
`␊
13+
> 1 | class Foo extends EventEmitter {}␊
14+
| ^^^^^^^^^^^^ Prefer \`EventTarget\` over \`EventEmitter\`.␊
15+
`
16+
17+
## Invalid #2
18+
1 | class Foo extends EventEmitter { someMethod() {} }
19+
20+
> Error 1/1
21+
22+
`␊
23+
> 1 | class Foo extends EventEmitter { someMethod() {} }␊
24+
| ^^^^^^^^^^^^ Prefer \`EventTarget\` over \`EventEmitter\`.␊
25+
`
26+
27+
## Invalid #3
28+
1 | const Foo = class extends EventEmitter {}
29+
30+
> Error 1/1
31+
32+
`␊
33+
> 1 | const Foo = class extends EventEmitter {}␊
34+
| ^^^^^^^^^^^^ Prefer \`EventTarget\` over \`EventEmitter\`.␊
35+
`
36+
37+
## Invalid #4
38+
1 | class Foo extends EventEmitter {
39+
2 | addListener() {}
40+
3 | removeListener() {}
41+
4 | }
42+
43+
> Error 1/1
44+
45+
`␊
46+
> 1 | class Foo extends EventEmitter {␊
47+
| ^^^^^^^^^^^^ Prefer \`EventTarget\` over \`EventEmitter\`.␊
48+
2 | addListener() {}␊
49+
3 | removeListener() {}␊
50+
4 | }␊
51+
`
52+
53+
## Invalid #1
54+
1 | new EventEmitter
55+
56+
> Error 1/1
57+
58+
`␊
59+
> 1 | new EventEmitter␊
60+
| ^^^^^^^^^^^^ Prefer \`EventTarget\` over \`EventEmitter\`.␊
61+
`
62+
63+
## Invalid #2
64+
1 | const emitter = new EventEmitter;
65+
66+
> Error 1/1
67+
68+
`␊
69+
> 1 | const emitter = new EventEmitter;␊
70+
| ^^^^^^^^^^^^ Prefer \`EventTarget\` over \`EventEmitter\`.␊
71+
`
409 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)