From 166524a5613d9f0e27cb5bc505fe83d86ff9e7b2 Mon Sep 17 00:00:00 2001 From: Gyubong Date: Mon, 9 May 2022 02:32:51 +0900 Subject: [PATCH] Add `prefer-event-target` rule (#1792) Co-authored-by: fisker Cheung Co-authored-by: Sindre Sorhus --- configs/recommended.js | 1 + docs/rules/prefer-event-target.md | 34 ++++++++++ readme.md | 1 + rules/prefer-event-target.js | 39 +++++++++++ test/prefer-event-target.mjs | 48 +++++++++++++ test/snapshots/prefer-event-target.mjs.md | 71 ++++++++++++++++++++ test/snapshots/prefer-event-target.mjs.snap | Bin 0 -> 409 bytes 7 files changed, 194 insertions(+) create mode 100644 docs/rules/prefer-event-target.md create mode 100644 rules/prefer-event-target.js create mode 100644 test/prefer-event-target.mjs create mode 100644 test/snapshots/prefer-event-target.mjs.md create mode 100644 test/snapshots/prefer-event-target.mjs.snap diff --git a/configs/recommended.js b/configs/recommended.js index 8e97273e22..c8d4c73bcb 100644 --- a/configs/recommended.js +++ b/configs/recommended.js @@ -78,6 +78,7 @@ module.exports = { 'unicorn/prefer-dom-node-dataset': 'error', 'unicorn/prefer-dom-node-remove': 'error', 'unicorn/prefer-dom-node-text-content': 'error', + 'unicorn/prefer-event-target': 'error', 'unicorn/prefer-export-from': 'error', 'unicorn/prefer-includes': 'error', 'unicorn/prefer-json-parse-buffer': 'off', diff --git a/docs/rules/prefer-event-target.md b/docs/rules/prefer-event-target.md new file mode 100644 index 0000000000..8660634a2d --- /dev/null +++ b/docs/rules/prefer-event-target.md @@ -0,0 +1,34 @@ +# Prefer `EventTarget` over `EventEmitter` + + + +βœ… *This rule is part of the [recommended](https://github.com/sindresorhus/eslint-plugin-unicorn#recommended-config) config.* + + +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. + +This rule reduces the bundle size and makes your code more cross-platform friendly. + +See the [differences](https://nodejs.org/api/events.html#eventtarget-and-event-api) between `EventEmitter` and `EventTarget`. + +## Fail + +```js +import {EventEmitter} from 'node:event'; + +class Foo extends EventEmitter {} +``` + +```js +const emitter = new EventEmitter(); +``` + +## Pass + +```js +class Foo extends EventTarget {} +``` + +```js +const target = new EventTarget(); +``` diff --git a/readme.md b/readme.md index c7cf97b09a..94a88f6444 100644 --- a/readme.md +++ b/readme.md @@ -118,6 +118,7 @@ Each rule has emojis denoting: | [prefer-dom-node-dataset](docs/rules/prefer-dom-node-dataset.md) | Prefer using `.dataset` on DOM elements over calling attribute methods. | βœ… | πŸ”§ | | | [prefer-dom-node-remove](docs/rules/prefer-dom-node-remove.md) | Prefer `childNode.remove()` over `parentNode.removeChild(childNode)`. | βœ… | πŸ”§ | πŸ’‘ | | [prefer-dom-node-text-content](docs/rules/prefer-dom-node-text-content.md) | Prefer `.textContent` over `.innerText`. | βœ… | | πŸ’‘ | +| [prefer-event-target](docs/rules/prefer-event-target.md) | Prefer `EventTarget` over `EventEmitter`. | βœ… | | | | [prefer-export-from](docs/rules/prefer-export-from.md) | Prefer `export…from` when re-exporting. | βœ… | πŸ”§ | πŸ’‘ | | [prefer-includes](docs/rules/prefer-includes.md) | Prefer `.includes()` over `.indexOf()` and `Array#some()` when checking for existence or non-existence. | βœ… | πŸ”§ | πŸ’‘ | | [prefer-json-parse-buffer](docs/rules/prefer-json-parse-buffer.md) | Prefer reading a JSON file as a buffer. | | πŸ”§ | | diff --git a/rules/prefer-event-target.js b/rules/prefer-event-target.js new file mode 100644 index 0000000000..c1bbcfca0b --- /dev/null +++ b/rules/prefer-event-target.js @@ -0,0 +1,39 @@ +'use strict'; +const {matches} = require('./selectors/index.js'); + +const MESSAGE_ID = 'prefer-event-target'; +const messages = { + [MESSAGE_ID]: 'Prefer `EventTarget` over `EventEmitter`.', +}; + +const selector = [ + 'Identifier', + '[name="EventEmitter"]', + matches([ + 'ClassDeclaration > .superClass', + 'ClassExpression > .superClass', + 'NewExpression > .callee', + ]), +].join(''); + +/** @param {import('eslint').Rule.RuleContext} context */ +const create = () => ({ + [selector](node) { + return { + node, + messageId: MESSAGE_ID, + }; + }, +}); + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + create, + meta: { + type: 'suggestion', + docs: { + description: 'Prefer `EventTarget` over `EventEmitter`.', + }, + messages, + }, +}; diff --git a/test/prefer-event-target.mjs b/test/prefer-event-target.mjs new file mode 100644 index 0000000000..dce958a4f4 --- /dev/null +++ b/test/prefer-event-target.mjs @@ -0,0 +1,48 @@ +import outdent from 'outdent'; +import {getTester} from './utils/test.mjs'; + +const {test} = getTester(import.meta); + +test.snapshot({ + valid: [ + 'class Foo {}', + 'class Foo extends OtherClass {}', + 'class Foo extends EventTarget {}', + 'const Foo = class extends EventTarget {}', + 'const Foo = class extends foo.EventTarget {}', + 'const Foo = class extends foo.bar.EventTarget {}', + 'class Foo extends foo.EventEmitter {}', + 'class Foo extends foo.bar.EventEmitter {}', + 'class EventEmitter extends Foo {}', + 'const Foo = class EventEmitter extends Foo {}', + 'new Foo(EventEmitter)', + 'new foo.EventEmitter()', + ], + invalid: [ + 'class Foo extends EventEmitter {}', + 'class Foo extends EventEmitter { someMethod() {} }', + 'const Foo = class extends EventEmitter {}', + outdent` + class Foo extends EventEmitter { + addListener() {} + removeListener() {} + } + `, + ], +}); + +test.snapshot({ + valid: [ + 'EventTarget()', + 'new EventTarget', + 'const target = new EventTarget;', + 'const target = EventTarget()', + 'const target = new Foo(EventEmitter);', + 'EventEmitter()', + 'const emitter = EventEmitter()', + ], + invalid: [ + 'new EventEmitter', + 'const emitter = new EventEmitter;', + ], +}); diff --git a/test/snapshots/prefer-event-target.mjs.md b/test/snapshots/prefer-event-target.mjs.md new file mode 100644 index 0000000000..d527bbc0f1 --- /dev/null +++ b/test/snapshots/prefer-event-target.mjs.md @@ -0,0 +1,71 @@ +# Snapshot report for `test/prefer-event-target.mjs` + +The actual snapshot is saved in `prefer-event-target.mjs.snap`. + +Generated by [AVA](https://avajs.dev). + +## Invalid #1 + 1 | class Foo extends EventEmitter {} + +> Error 1/1 + + `␊ + > 1 | class Foo extends EventEmitter {}␊ + | ^^^^^^^^^^^^ Prefer \`EventTarget\` over \`EventEmitter\`.␊ + ` + +## Invalid #2 + 1 | class Foo extends EventEmitter { someMethod() {} } + +> Error 1/1 + + `␊ + > 1 | class Foo extends EventEmitter { someMethod() {} }␊ + | ^^^^^^^^^^^^ Prefer \`EventTarget\` over \`EventEmitter\`.␊ + ` + +## Invalid #3 + 1 | const Foo = class extends EventEmitter {} + +> Error 1/1 + + `␊ + > 1 | const Foo = class extends EventEmitter {}␊ + | ^^^^^^^^^^^^ Prefer \`EventTarget\` over \`EventEmitter\`.␊ + ` + +## Invalid #4 + 1 | class Foo extends EventEmitter { + 2 | addListener() {} + 3 | removeListener() {} + 4 | } + +> Error 1/1 + + `␊ + > 1 | class Foo extends EventEmitter {␊ + | ^^^^^^^^^^^^ Prefer \`EventTarget\` over \`EventEmitter\`.␊ + 2 | addListener() {}␊ + 3 | removeListener() {}␊ + 4 | }␊ + ` + +## Invalid #1 + 1 | new EventEmitter + +> Error 1/1 + + `␊ + > 1 | new EventEmitter␊ + | ^^^^^^^^^^^^ Prefer \`EventTarget\` over \`EventEmitter\`.␊ + ` + +## Invalid #2 + 1 | const emitter = new EventEmitter; + +> Error 1/1 + + `␊ + > 1 | const emitter = new EventEmitter;␊ + | ^^^^^^^^^^^^ Prefer \`EventTarget\` over \`EventEmitter\`.␊ + ` diff --git a/test/snapshots/prefer-event-target.mjs.snap b/test/snapshots/prefer-event-target.mjs.snap new file mode 100644 index 0000000000000000000000000000000000000000..347f062ac34ed430e6ffd66ced95bf1dfb50041e GIT binary patch literal 409 zcmV;K0cQR|RzV7JG2`B^