Skip to content

Commit e9aec7f

Browse files
43081jbaseballyama
andauthored
feat: add no-add-event-listener rule (#1197)
Co-authored-by: Yuichiro Yamashita <[email protected]>
1 parent a51e8c7 commit e9aec7f

File tree

15 files changed

+630
-0
lines changed

15 files changed

+630
-0
lines changed

.changeset/sad-points-strive.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'eslint-plugin-svelte': minor
3+
---
4+
5+
Added `no-add-event-listener` rule to disallow usages of `addEventListener`

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,7 @@ These rules relate to better ways of doing things to help you avoid problems:
294294
|:--------|:------------|:---|
295295
| [svelte/block-lang](https://sveltejs.github.io/eslint-plugin-svelte/rules/block-lang/) | disallows the use of languages other than those specified in the configuration for the lang attribute of `<script>` and `<style>` blocks. | :bulb: |
296296
| [svelte/button-has-type](https://sveltejs.github.io/eslint-plugin-svelte/rules/button-has-type/) | disallow usage of button without an explicit type attribute | |
297+
| [svelte/no-add-event-listener](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-add-event-listener/) | Warns against the use of `addEventListener` | :bulb: |
297298
| [svelte/no-at-debug-tags](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-at-debug-tags/) | disallow the use of `{@debug}` | :star::bulb: |
298299
| [svelte/no-ignored-unsubscribe](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-ignored-unsubscribe/) | disallow ignoring the unsubscribe method returned by the `subscribe()` on Svelte stores. | |
299300
| [svelte/no-immutable-reactive-statements](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-immutable-reactive-statements/) | disallow reactive statements that don't reference reactive values. | :star: |

docs/rules.md

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ These rules relate to better ways of doing things to help you avoid problems:
5151
| :--------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------- | :------------- |
5252
| [svelte/block-lang](./rules/block-lang.md) | disallows the use of languages other than those specified in the configuration for the lang attribute of `<script>` and `<style>` blocks. | :bulb: |
5353
| [svelte/button-has-type](./rules/button-has-type.md) | disallow usage of button without an explicit type attribute | |
54+
| [svelte/no-add-event-listener](./rules/no-add-event-listener.md) | Warns against the use of `addEventListener` | :bulb: |
5455
| [svelte/no-at-debug-tags](./rules/no-at-debug-tags.md) | disallow the use of `{@debug}` | :star::bulb: |
5556
| [svelte/no-ignored-unsubscribe](./rules/no-ignored-unsubscribe.md) | disallow ignoring the unsubscribe method returned by the `subscribe()` on Svelte stores. | |
5657
| [svelte/no-immutable-reactive-statements](./rules/no-immutable-reactive-statements.md) | disallow reactive statements that don't reference reactive values. | :star: |

docs/rules/no-add-event-listener.md

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
---
2+
pageClass: 'rule-details'
3+
sidebarDepth: 0
4+
title: 'svelte/no-add-event-listener'
5+
description: 'Warns against the use of `addEventListener`'
6+
---
7+
8+
# svelte/no-add-event-listener
9+
10+
> Warns against the use of `addEventListener`
11+
12+
- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> **_This rule has not been released yet._** </badge>
13+
- :bulb: Some problems reported by this rule are manually fixable by editor [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions).
14+
15+
## :book: Rule Details
16+
17+
Svelte relies on event delegation for performance and predictable handler order. Calling `addEventListener` inside a component skips this mechanism. This rule reports any call to `addEventListener` suggests converting to the `on()` helper from `svelte/events`.
18+
19+
<!--eslint-skip-->
20+
21+
```svelte
22+
<!-- ✓ GOOD -->
23+
<script>
24+
/* eslint svelte/no-add-event-listener: "error" */
25+
on(window, 'resize', handler);
26+
</script>
27+
```
28+
29+
<!--eslint-skip-->
30+
31+
```svelte
32+
<!-- ✗ BAD -->
33+
<script>
34+
/* eslint svelte/no-add-event-listener: "error" */
35+
window.addEventListener('resize', handler);
36+
</script>
37+
```
38+
39+
## :books: Further reading
40+
41+
- [svelte - event delegation]
42+
- [svelte/events `on` documentation]
43+
44+
[svelte - event delegation]: https://svelte.dev/docs/svelte/basic-markup#Events-Event-delegation
45+
[svelte/events `on` documentation]: https://svelte.dev/docs/svelte/svelte-events#on
46+
47+
## :mag: Implementation
48+
49+
- [Rule source](https://github.com/sveltejs/eslint-plugin-svelte/blob/main/packages/eslint-plugin-svelte/src/rules/no-add-event-listener.ts)
50+
- [Test source](https://github.com/sveltejs/eslint-plugin-svelte/blob/main/packages/eslint-plugin-svelte/tests/src/rules/no-add-event-listener.ts)

packages/eslint-plugin-svelte/src/rule-types.ts

+5
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,11 @@ export interface RuleOptions {
9999
* @see https://sveltejs.github.io/eslint-plugin-svelte/rules/mustache-spacing/
100100
*/
101101
'svelte/mustache-spacing'?: Linter.RuleEntry<SvelteMustacheSpacing>
102+
/**
103+
* Warns against the use of `addEventListener`
104+
* @see https://sveltejs.github.io/eslint-plugin-svelte/rules/no-add-event-listener/
105+
*/
106+
'svelte/no-add-event-listener'?: Linter.RuleEntry<[]>
102107
/**
103108
* disallow the use of `{@debug}`
104109
* @see https://sveltejs.github.io/eslint-plugin-svelte/rules/no-at-debug-tags/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import type { TSESTree } from '@typescript-eslint/types';
2+
3+
import { createRule } from '../utils/index.js';
4+
import type { SuggestionReportDescriptor } from '../types.js';
5+
6+
export default createRule('no-add-event-listener', {
7+
meta: {
8+
docs: {
9+
description: 'Warns against the use of `addEventListener`',
10+
category: 'Best Practices',
11+
recommended: false
12+
},
13+
hasSuggestions: true,
14+
schema: [],
15+
messages: {
16+
unexpected:
17+
'Do not use `addEventListener`. Use the `on` function from `svelte/events` instead.'
18+
},
19+
type: 'suggestion',
20+
conditions: [
21+
{
22+
svelteVersions: ['5']
23+
}
24+
]
25+
},
26+
create(context) {
27+
return {
28+
CallExpression(node: TSESTree.CallExpression) {
29+
const { callee } = node;
30+
let target: string | null = null;
31+
32+
if (
33+
callee.type === 'MemberExpression' &&
34+
callee.property.type === 'Identifier' &&
35+
callee.property.name === 'addEventListener'
36+
) {
37+
target = context.sourceCode.getText(callee.object);
38+
} else if (callee.type === 'Identifier' && callee.name === 'addEventListener') {
39+
target = 'window';
40+
}
41+
42+
if (target === null) {
43+
return;
44+
}
45+
46+
const openParen = context.sourceCode.getTokenAfter(callee);
47+
const suggest: SuggestionReportDescriptor[] = [];
48+
49+
if (openParen !== null) {
50+
suggest.push({
51+
desc: 'Use `on` from `svelte/events` instead',
52+
fix(fixer) {
53+
return [
54+
fixer.replaceText(callee, 'on'),
55+
fixer.insertTextAfter(openParen, `${target}, `)
56+
];
57+
}
58+
});
59+
}
60+
61+
context.report({
62+
node,
63+
messageId: 'unexpected',
64+
suggest
65+
});
66+
}
67+
};
68+
}
69+
});

packages/eslint-plugin-svelte/src/utils/rules.ts

+2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import indent from '../rules/indent.js';
1919
import infiniteReactiveLoop from '../rules/infinite-reactive-loop.js';
2020
import maxAttributesPerLine from '../rules/max-attributes-per-line.js';
2121
import mustacheSpacing from '../rules/mustache-spacing.js';
22+
import noAddEventListener from '../rules/no-add-event-listener.js';
2223
import noAtDebugTags from '../rules/no-at-debug-tags.js';
2324
import noAtHtmlTags from '../rules/no-at-html-tags.js';
2425
import noDomManipulating from '../rules/no-dom-manipulating.js';
@@ -96,6 +97,7 @@ export const rules = [
9697
infiniteReactiveLoop,
9798
maxAttributesPerLine,
9899
mustacheSpacing,
100+
noAddEventListener,
99101
noAtDebugTags,
100102
noAtHtmlTags,
101103
noDomManipulating,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"svelte": ">=5.0.0-0"
3+
}

0 commit comments

Comments
 (0)