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