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 ` +``` + + + +```svelte + + +``` + +## :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..95e553c62 --- /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 || args.length > 3) { + 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..0318160dc --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-add-event-listener/invalid/test01-errors.yaml @@ -0,0 +1,328 @@ +- 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: | + + + +- 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 new file mode 100644 index 000000000..790e022f7 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-add-event-listener/invalid/test01-input.svelte @@ -0,0 +1,34 @@ + + + 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
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'));