From 8961878997f491052a983d9f7c51cd13216b4fae Mon Sep 17 00:00:00 2001
From: James Garbutt <43081j@users.noreply.github.com>
Date: Thu, 17 Apr 2025 09:08:49 +0100
Subject: [PATCH 1/2] feat: add `no-add-event-listener` rule
---
README.md | 1 +
docs/rules.md | 1 +
docs/rules/no-add-event-listener.md | 31 ++
.../eslint-plugin-svelte/src/rule-types.ts | 5 +
.../src/rules/no-add-event-listener.ts | 74 +++++
.../eslint-plugin-svelte/src/utils/rules.ts | 2 +
.../invalid/_requirements.json | 3 +
.../invalid/test01-errors.yaml | 266 ++++++++++++++++++
.../invalid/test01-input.svelte | 31 ++
.../valid/_requirements.json | 3 +
.../valid/test01-input.svelte | 19 ++
.../tests/src/rules/no-add-event-listener.ts | 12 +
12 files changed, 448 insertions(+)
create mode 100644 docs/rules/no-add-event-listener.md
create mode 100644 packages/eslint-plugin-svelte/src/rules/no-add-event-listener.ts
create mode 100644 packages/eslint-plugin-svelte/tests/fixtures/rules/no-add-event-listener/invalid/_requirements.json
create mode 100644 packages/eslint-plugin-svelte/tests/fixtures/rules/no-add-event-listener/invalid/test01-errors.yaml
create mode 100644 packages/eslint-plugin-svelte/tests/fixtures/rules/no-add-event-listener/invalid/test01-input.svelte
create mode 100644 packages/eslint-plugin-svelte/tests/fixtures/rules/no-add-event-listener/valid/_requirements.json
create mode 100644 packages/eslint-plugin-svelte/tests/fixtures/rules/no-add-event-listener/valid/test01-input.svelte
create mode 100644 packages/eslint-plugin-svelte/tests/src/rules/no-add-event-listener.ts
diff --git a/README.md b/README.md
index c379a3d82..6e5ea8c7d 100644
--- a/README.md
+++ b/README.md
@@ -294,6 +294,7 @@ These rules relate to better ways of doing things to help you avoid problems:
|:--------|:------------|:---|
| [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 `
+```
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/sveltejs/eslint-plugin-svelte/blob/main/packages/eslint-plugin-svelte/src/rules/no-add-event-listener.ts)
+- [Test source](https://github.com/sveltejs/eslint-plugin-svelte/blob/main/packages/eslint-plugin-svelte/tests/src/rules/no-add-event-listener.ts)
diff --git a/packages/eslint-plugin-svelte/src/rule-types.ts b/packages/eslint-plugin-svelte/src/rule-types.ts
index 7b5cf238c..670acbd53 100644
--- a/packages/eslint-plugin-svelte/src/rule-types.ts
+++ b/packages/eslint-plugin-svelte/src/rule-types.ts
@@ -99,6 +99,11 @@ export interface RuleOptions {
* @see https://sveltejs.github.io/eslint-plugin-svelte/rules/mustache-spacing/
*/
'svelte/mustache-spacing'?: Linter.RuleEntry
+ /**
+ * Warns against the use of `addEventListener`
+ * @see https://sveltejs.github.io/eslint-plugin-svelte/rules/no-add-event-listener/
+ */
+ 'svelte/no-add-event-listener'?: Linter.RuleEntry<[]>
/**
* disallow the use of `{@debug}`
* @see https://sveltejs.github.io/eslint-plugin-svelte/rules/no-at-debug-tags/
diff --git a/packages/eslint-plugin-svelte/src/rules/no-add-event-listener.ts b/packages/eslint-plugin-svelte/src/rules/no-add-event-listener.ts
new file mode 100644
index 000000000..fb478ce72
--- /dev/null
+++ b/packages/eslint-plugin-svelte/src/rules/no-add-event-listener.ts
@@ -0,0 +1,74 @@
+import type { TSESTree } from '@typescript-eslint/types';
+
+import { createRule } from '../utils/index.js';
+import type { SuggestionReportDescriptor } from '../types.js';
+
+export default createRule('no-add-event-listener', {
+ meta: {
+ docs: {
+ description: 'Warns against the use of `addEventListener`',
+ category: 'Best Practices',
+ recommended: false,
+ default: 'warn'
+ },
+ hasSuggestions: true,
+ schema: [],
+ messages: {
+ unexpected:
+ 'Do not use `addEventListener`. Use the `on` function from `svelte/events` instead.'
+ },
+ type: 'suggestion',
+ conditions: [
+ {
+ svelteVersions: ['5']
+ }
+ ]
+ },
+ create(context) {
+ return {
+ CallExpression(node: TSESTree.CallExpression) {
+ const { callee, arguments: args } = node;
+ let target: string | null = null;
+
+ if (args.length !== 2) {
+ return;
+ }
+
+ if (
+ callee.type === 'MemberExpression' &&
+ callee.property.type === 'Identifier' &&
+ callee.property.name === 'addEventListener'
+ ) {
+ target = context.sourceCode.getText(callee.object);
+ } else if (callee.type === 'Identifier' && callee.name === 'addEventListener') {
+ target = 'window';
+ }
+
+ if (target === null) {
+ return;
+ }
+
+ const openParen = context.sourceCode.getTokenAfter(callee);
+ const suggest: SuggestionReportDescriptor[] = [];
+
+ if (openParen !== null) {
+ suggest.push({
+ desc: 'Use `on` from `svelte/events` instead',
+ fix(fixer) {
+ return [
+ fixer.replaceText(callee, 'on'),
+ fixer.insertTextAfter(openParen, `${target}, `)
+ ];
+ }
+ });
+ }
+
+ context.report({
+ node,
+ messageId: 'unexpected',
+ suggest
+ });
+ }
+ };
+ }
+});
diff --git a/packages/eslint-plugin-svelte/src/utils/rules.ts b/packages/eslint-plugin-svelte/src/utils/rules.ts
index 3151d17f1..9a737cdff 100644
--- a/packages/eslint-plugin-svelte/src/utils/rules.ts
+++ b/packages/eslint-plugin-svelte/src/utils/rules.ts
@@ -19,6 +19,7 @@ import indent from '../rules/indent.js';
import infiniteReactiveLoop from '../rules/infinite-reactive-loop.js';
import maxAttributesPerLine from '../rules/max-attributes-per-line.js';
import mustacheSpacing from '../rules/mustache-spacing.js';
+import noAddEventListener from '../rules/no-add-event-listener.js';
import noAtDebugTags from '../rules/no-at-debug-tags.js';
import noAtHtmlTags from '../rules/no-at-html-tags.js';
import noDomManipulating from '../rules/no-dom-manipulating.js';
@@ -94,6 +95,7 @@ export const rules = [
infiniteReactiveLoop,
maxAttributesPerLine,
mustacheSpacing,
+ noAddEventListener,
noAtDebugTags,
noAtHtmlTags,
noDomManipulating,
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-add-event-listener/invalid/_requirements.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-add-event-listener/invalid/_requirements.json
new file mode 100644
index 000000000..0192b1098
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-add-event-listener/invalid/_requirements.json
@@ -0,0 +1,3 @@
+{
+ "svelte": ">=5.0.0-0"
+}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-add-event-listener/invalid/test01-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-add-event-listener/invalid/test01-errors.yaml
new file mode 100644
index 000000000..7ae3e6dc1
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-add-event-listener/invalid/test01-errors.yaml
@@ -0,0 +1,266 @@
+- message: Do not use `addEventListener`. Use the `on` function from
+ `svelte/events` instead.
+ line: 17
+ column: 3
+ suggestions:
+ - desc: Use `on` from `svelte/events` instead
+ output: |
+
+
+
+- message: Do not use `addEventListener`. Use the `on` function from
+ `svelte/events` instead.
+ line: 18
+ column: 3
+ suggestions:
+ - desc: Use `on` from `svelte/events` instead
+ output: |
+
+
+
+- message: Do not use `addEventListener`. Use the `on` function from
+ `svelte/events` instead.
+ line: 21
+ column: 2
+ suggestions:
+ - desc: Use `on` from `svelte/events` instead
+ output: |
+
+
+
+- message: Do not use `addEventListener`. Use the `on` function from
+ `svelte/events` instead.
+ line: 22
+ column: 2
+ suggestions:
+ - desc: Use `on` from `svelte/events` instead
+ output: |
+
+
+
+- message: Do not use `addEventListener`. Use the `on` function from
+ `svelte/events` instead.
+ line: 23
+ column: 2
+ suggestions:
+ - desc: Use `on` from `svelte/events` instead
+ output: |
+
+
+
+- message: Do not use `addEventListener`. Use the `on` function from
+ `svelte/events` instead.
+ line: 26
+ column: 2
+ suggestions:
+ - desc: Use `on` from `svelte/events` instead
+ output: |
+
+
+
+- message: Do not use `addEventListener`. Use the `on` function from
+ `svelte/events` instead.
+ line: 28
+ column: 2
+ suggestions:
+ - desc: Use `on` from `svelte/events` instead
+ output: |
+
+
+
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-add-event-listener/invalid/test01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-add-event-listener/invalid/test01-input.svelte
new file mode 100644
index 000000000..d51aeca93
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-add-event-listener/invalid/test01-input.svelte
@@ -0,0 +1,31 @@
+
+
+
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-add-event-listener/valid/_requirements.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-add-event-listener/valid/_requirements.json
new file mode 100644
index 000000000..0192b1098
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-add-event-listener/valid/_requirements.json
@@ -0,0 +1,3 @@
+{
+ "svelte": ">=5.0.0-0"
+}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-add-event-listener/valid/test01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-add-event-listener/valid/test01-input.svelte
new file mode 100644
index 000000000..0b48a7d94
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-add-event-listener/valid/test01-input.svelte
@@ -0,0 +1,19 @@
+
+
+
diff --git a/packages/eslint-plugin-svelte/tests/src/rules/no-add-event-listener.ts b/packages/eslint-plugin-svelte/tests/src/rules/no-add-event-listener.ts
new file mode 100644
index 000000000..a2b5928d1
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/src/rules/no-add-event-listener.ts
@@ -0,0 +1,12 @@
+import { RuleTester } from '../../utils/eslint-compat.js';
+import rule from '../../../src/rules/no-add-event-listener.js';
+import { loadTestCases } from '../../utils/utils.js';
+
+const tester = new RuleTester({
+ languageOptions: {
+ ecmaVersion: 2020,
+ sourceType: 'module'
+ }
+});
+
+tester.run('no-add-event-listener', rule as any, loadTestCases('no-add-event-listener'));
From e13635bbf1f8e5714786c54366babda1eb4f5695 Mon Sep 17 00:00:00 2001
From: James Garbutt <43081j@users.noreply.github.com>
Date: Mon, 21 Apr 2025 13:51:17 +0100
Subject: [PATCH 2/2] test: add options test cases
---
docs/rules/no-add-event-listener.md | 13 +++-
.../src/rules/no-add-event-listener.ts | 2 +-
.../invalid/test01-errors.yaml | 62 +++++++++++++++++++
.../invalid/test01-input.svelte | 3 +
.../invalid/typescript01-errors.yaml | 17 +++++
.../invalid/typescript01-input.svelte | 10 +++
6 files changed, 105 insertions(+), 2 deletions(-)
create mode 100644 packages/eslint-plugin-svelte/tests/fixtures/rules/no-add-event-listener/invalid/typescript01-errors.yaml
create mode 100644 packages/eslint-plugin-svelte/tests/fixtures/rules/no-add-event-listener/invalid/typescript01-input.svelte
diff --git a/docs/rules/no-add-event-listener.md b/docs/rules/no-add-event-listener.md
index 0371457e1..2a81c9aff 100644
--- a/docs/rules/no-add-event-listener.md
+++ b/docs/rules/no-add-event-listener.md
@@ -19,9 +19,20 @@ This rule reports usages of `addEventListener`:
```svelte
+
+```
+
+
+
+```svelte
+
+
```
diff --git a/packages/eslint-plugin-svelte/src/rules/no-add-event-listener.ts b/packages/eslint-plugin-svelte/src/rules/no-add-event-listener.ts
index fb478ce72..95e553c62 100644
--- a/packages/eslint-plugin-svelte/src/rules/no-add-event-listener.ts
+++ b/packages/eslint-plugin-svelte/src/rules/no-add-event-listener.ts
@@ -30,7 +30,7 @@ export default createRule('no-add-event-listener', {
const { callee, arguments: args } = node;
let target: string | null = null;
- if (args.length !== 2) {
+ if (args.length < 2 || args.length > 3) {
return;
}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-add-event-listener/invalid/test01-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-add-event-listener/invalid/test01-errors.yaml
index 7ae3e6dc1..0318160dc 100644
--- a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-add-event-listener/invalid/test01-errors.yaml
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-add-event-listener/invalid/test01-errors.yaml
@@ -33,6 +33,9 @@
window.addEventListener ('message', handler);
// with a comment
window.addEventListener/* foo */('message', handler);
+
+ // with options
+ window.addEventListener('message', handler, { once: true });
@@ -71,6 +74,9 @@
window.addEventListener ('message', handler);
// with a comment
window.addEventListener/* foo */('message', handler);
+
+ // with options
+ window.addEventListener('message', handler, { once: true });
@@ -109,6 +115,9 @@
window.addEventListener ('message', handler);
// with a comment
window.addEventListener/* foo */('message', handler);
+
+ // with options
+ window.addEventListener('message', handler, { once: true });
@@ -147,6 +156,9 @@
window.addEventListener ('message', handler);
// with a comment
window.addEventListener/* foo */('message', handler);
+
+ // with options
+ window.addEventListener('message', handler, { once: true });
@@ -185,6 +197,9 @@
window.addEventListener ('message', handler);
// with a comment
window.addEventListener/* foo */('message', handler);
+
+ // with options
+ window.addEventListener('message', handler, { once: true });
@@ -223,6 +238,9 @@
on (window, 'message', handler);
// with a comment
window.addEventListener/* foo */('message', handler);
+
+ // with options
+ window.addEventListener('message', handler, { once: true });
@@ -261,6 +279,50 @@
window.addEventListener ('message', handler);
// with a comment
on/* foo */(window, 'message', handler);
+
+ // with options
+ window.addEventListener('message', handler, { once: true });
+
+
+
+- message: Do not use `addEventListener`. Use the `on` function from
+ `svelte/events` instead.
+ line: 31
+ column: 2
+ suggestions:
+ - desc: Use `on` from `svelte/events` instead
+ output: |
+
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-add-event-listener/invalid/test01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-add-event-listener/invalid/test01-input.svelte
index d51aeca93..790e022f7 100644
--- a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-add-event-listener/invalid/test01-input.svelte
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-add-event-listener/invalid/test01-input.svelte
@@ -26,6 +26,9 @@
window.addEventListener ('message', handler);
// with a comment
window.addEventListener/* foo */('message', handler);
+
+ // with options
+ window.addEventListener('message', handler, { once: true });
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-add-event-listener/invalid/typescript01-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-add-event-listener/invalid/typescript01-errors.yaml
new file mode 100644
index 000000000..b1c8fc473
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-add-event-listener/invalid/typescript01-errors.yaml
@@ -0,0 +1,17 @@
+- message: Do not use `addEventListener`. Use the `on` function from
+ `svelte/events` instead.
+ line: 6
+ column: 2
+ suggestions:
+ - desc: Use `on` from `svelte/events` instead
+ output: |
+
+
+ Hello
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-add-event-listener/invalid/typescript01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-add-event-listener/invalid/typescript01-input.svelte
new file mode 100644
index 000000000..180366a12
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-add-event-listener/invalid/typescript01-input.svelte
@@ -0,0 +1,10 @@
+
+
+Hello