From 5da98c94f452c8436f26af4172e095fd91f66e1a Mon Sep 17 00:00:00 2001 From: Miquel de Domingo i Giralt Date: Wed, 27 Nov 2024 13:36:48 +0100 Subject: [PATCH] feat: add rule `no-deprecated-raw-special-elements` (#918) ~Adds a new Svelte 5 specific rule that ensure special elements are used with `svelte:` prefix. This rule will help on migrating from Svelte 4, as such elements were supported without the prefix in Svelte 4.~ Adds a new rule that recommends not using raw special elements and fixes to using the `svelte:` prefix. Raw special elements are deprecated from v5 on. Closes #913 --------- Co-authored-by: Yosuke Ota --- .changeset/lazy-eyes-wait.md | 5 ++ README.md | 1 + docs/rules.md | 1 + .../no-deprecated-raw-special-elements.md | 52 +++++++++++++++++++ .../eslint-plugin-svelte/src/rule-types.ts | 5 ++ .../no-deprecated-raw-special-elements.ts | 47 +++++++++++++++++ .../eslint-plugin-svelte/src/utils/rules.ts | 2 + .../invalid/test01-errors.yaml | 24 +++++++++ .../invalid/test01-input.svelte | 6 +++ .../invalid/test01-output.svelte | 6 +++ .../valid/test01-input.svelte | 8 +++ .../no-deprecated-raw-special-elements.ts | 12 +++++ 12 files changed, 169 insertions(+) create mode 100644 .changeset/lazy-eyes-wait.md create mode 100644 docs/rules/no-deprecated-raw-special-elements.md create mode 100644 packages/eslint-plugin-svelte/src/rules/no-deprecated-raw-special-elements.ts create mode 100644 packages/eslint-plugin-svelte/tests/fixtures/rules/no-deprecated-raw-special-elements/invalid/test01-errors.yaml create mode 100644 packages/eslint-plugin-svelte/tests/fixtures/rules/no-deprecated-raw-special-elements/invalid/test01-input.svelte create mode 100644 packages/eslint-plugin-svelte/tests/fixtures/rules/no-deprecated-raw-special-elements/invalid/test01-output.svelte create mode 100644 packages/eslint-plugin-svelte/tests/fixtures/rules/no-deprecated-raw-special-elements/valid/test01-input.svelte create mode 100644 packages/eslint-plugin-svelte/tests/src/rules/no-deprecated-raw-special-elements.ts diff --git a/.changeset/lazy-eyes-wait.md b/.changeset/lazy-eyes-wait.md new file mode 100644 index 000000000..74f2c8611 --- /dev/null +++ b/.changeset/lazy-eyes-wait.md @@ -0,0 +1,5 @@ +--- +'eslint-plugin-svelte': minor +--- + +Added new `no-deprecated-raw-special-elements` rule diff --git a/README.md b/README.md index 9682cbf66..7c569527b 100644 --- a/README.md +++ b/README.md @@ -380,6 +380,7 @@ These rules relate to possible syntax or logic errors in Svelte code: | Rule ID | Description | | |:--------|:------------|:---| | [svelte/infinite-reactive-loop](https://sveltejs.github.io/eslint-plugin-svelte/rules/infinite-reactive-loop/) | Svelte runtime prevents calling the same reactive statement twice in a microtask. But between different microtask, it doesn't prevent. | | +| [svelte/no-deprecated-raw-special-elements](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-deprecated-raw-special-elements/) | Recommends not using raw special elements in Svelte versions previous to 5. | :wrench: | | [svelte/no-dom-manipulating](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-dom-manipulating/) | disallow DOM manipulating | | | [svelte/no-dupe-else-if-blocks](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-dupe-else-if-blocks/) | disallow duplicate conditions in `{#if}` / `{:else if}` chains | :star: | | [svelte/no-dupe-on-directives](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-dupe-on-directives/) | disallow duplicate `on:` directives | | diff --git a/docs/rules.md b/docs/rules.md index fb067e57c..29701ff7e 100644 --- a/docs/rules.md +++ b/docs/rules.md @@ -17,6 +17,7 @@ These rules relate to possible syntax or logic errors in Svelte code: | Rule ID | Description | | | :------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------- | :------------- | | [svelte/infinite-reactive-loop](./rules/infinite-reactive-loop.md) | Svelte runtime prevents calling the same reactive statement twice in a microtask. But between different microtask, it doesn't prevent. | | +| [svelte/no-deprecated-raw-special-elements](./rules/no-deprecated-raw-special-elements.md) | Recommends not using raw special elements in Svelte versions previous to 5. | :wrench: | | [svelte/no-dom-manipulating](./rules/no-dom-manipulating.md) | disallow DOM manipulating | | | [svelte/no-dupe-else-if-blocks](./rules/no-dupe-else-if-blocks.md) | disallow duplicate conditions in `{#if}` / `{:else if}` chains | :star: | | [svelte/no-dupe-on-directives](./rules/no-dupe-on-directives.md) | disallow duplicate `on:` directives | | diff --git a/docs/rules/no-deprecated-raw-special-elements.md b/docs/rules/no-deprecated-raw-special-elements.md new file mode 100644 index 000000000..4bf863ae6 --- /dev/null +++ b/docs/rules/no-deprecated-raw-special-elements.md @@ -0,0 +1,52 @@ +--- +pageClass: 'rule-details' +sidebarDepth: 0 +title: 'svelte/no-deprecated-raw-special-elements' +description: 'Recommends not using raw special elements in Svelte versions previous to 5.' +--- + +# svelte/no-deprecated-raw-special-elements + +> Recommends not using raw special elements in Svelte versions previous to 5. + +- :exclamation: **_This rule has not been released yet._** +- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule. + +## :book: Rule Details + +This rule reports the usage of `head`, `body`, `window`, `document`, `element` and `options` HTML elements. These elements were valid in in versions proior to 5, but since Svelte 5 they must be used with `svelte:`. + + + + + +```svelte + + + + + Valid + + + + + Invalid + +``` + + + +## :wrench: Options + +Nothing. + +## :books: Further Reading + +- See special elements section in [Svelte docs](https://svelte.dev/docs/svelte/svelte-window) + +## :mag: Implementation + +- [Rule source](https://github.com/sveltejs/eslint-plugin-svelte/blob/main/packages/eslint-plugin-svelte/src/rules/no-deprecated-raw-special-elements.ts) +- [Test source](https://github.com/sveltejs/eslint-plugin-svelte/blob/main/packages/eslint-plugin-svelte/tests/src/rules/no-deprecated-raw-special-elements.ts) diff --git a/packages/eslint-plugin-svelte/src/rule-types.ts b/packages/eslint-plugin-svelte/src/rule-types.ts index 8e747b6e4..8b7cbe80c 100644 --- a/packages/eslint-plugin-svelte/src/rule-types.ts +++ b/packages/eslint-plugin-svelte/src/rule-types.ts @@ -104,6 +104,11 @@ export interface RuleOptions { * @see https://sveltejs.github.io/eslint-plugin-svelte/rules/no-at-html-tags/ */ 'svelte/no-at-html-tags'?: Linter.RuleEntry<[]> + /** + * Recommends not using raw special elements in Svelte versions previous to 5. + * @see https://sveltejs.github.io/eslint-plugin-svelte/rules/no-deprecated-raw-special-elements/ + */ + 'svelte/no-deprecated-raw-special-elements'?: Linter.RuleEntry<[]> /** * disallow DOM manipulating * @see https://sveltejs.github.io/eslint-plugin-svelte/rules/no-dom-manipulating/ diff --git a/packages/eslint-plugin-svelte/src/rules/no-deprecated-raw-special-elements.ts b/packages/eslint-plugin-svelte/src/rules/no-deprecated-raw-special-elements.ts new file mode 100644 index 000000000..d47ef72e1 --- /dev/null +++ b/packages/eslint-plugin-svelte/src/rules/no-deprecated-raw-special-elements.ts @@ -0,0 +1,47 @@ +import type { AST } from 'svelte-eslint-parser'; +import { createRule } from '../utils'; + +const INVALID_HTML_ELEMENTS = ['head', 'body', 'window', 'document', 'element', 'options']; +const VALID_PREFIX = 'svelte:'; + +export default createRule('no-deprecated-raw-special-elements', { + meta: { + docs: { + description: 'Recommends not using raw special elements in Svelte versions previous to 5.', + category: 'Possible Errors', + // TODO: Switch to recommended in the major version + recommended: false + }, + schema: [], + messages: { + deprecatedElement: + 'Special {{name}} element is deprecated in v5, use svelte:{{name}} instead.' + }, + type: 'problem', // 'problem', or 'layout', + fixable: 'code' + }, + create(context) { + return { + 'SvelteElement[kind="html"]'(node: AST.SvelteHTMLElement) { + const { name } = node.name; + if (INVALID_HTML_ELEMENTS.includes(name)) { + context.report({ + node, + messageId: 'deprecatedElement', + data: { name }, + *fix(fixer) { + const { endTag } = node; + yield fixer.insertTextBeforeRange([node.range[0] + 1, node.range[1]], VALID_PREFIX); + if (endTag) { + yield fixer.insertTextBeforeRange( + [endTag.range[0] + 2, endTag.range[1]], + VALID_PREFIX + ); + } + } + }); + } + } + }; + } +}); diff --git a/packages/eslint-plugin-svelte/src/utils/rules.ts b/packages/eslint-plugin-svelte/src/utils/rules.ts index 5bb5a080c..862db8b99 100644 --- a/packages/eslint-plugin-svelte/src/utils/rules.ts +++ b/packages/eslint-plugin-svelte/src/utils/rules.ts @@ -20,6 +20,7 @@ import maxAttributesPerLine from '../rules/max-attributes-per-line'; import mustacheSpacing from '../rules/mustache-spacing'; import noAtDebugTags from '../rules/no-at-debug-tags'; import noAtHtmlTags from '../rules/no-at-html-tags'; +import noDeprecatedRawSpecialElements from '../rules/no-deprecated-raw-special-elements'; import noDomManipulating from '../rules/no-dom-manipulating'; import noDupeElseIfBlocks from '../rules/no-dupe-else-if-blocks'; import noDupeOnDirectives from '../rules/no-dupe-on-directives'; @@ -87,6 +88,7 @@ export const rules = [ mustacheSpacing, noAtDebugTags, noAtHtmlTags, + noDeprecatedRawSpecialElements, noDomManipulating, noDupeElseIfBlocks, noDupeOnDirectives, diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-deprecated-raw-special-elements/invalid/test01-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-deprecated-raw-special-elements/invalid/test01-errors.yaml new file mode 100644 index 000000000..2acb009df --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-deprecated-raw-special-elements/invalid/test01-errors.yaml @@ -0,0 +1,24 @@ +- message: Special head element is deprecated in v5, use svelte:head instead. + line: 1 + column: 1 + suggestions: null +- message: Special body element is deprecated in v5, use svelte:body instead. + line: 2 + column: 1 + suggestions: null +- message: Special window element is deprecated in v5, use svelte:window instead. + line: 3 + column: 1 + suggestions: null +- message: Special document element is deprecated in v5, use svelte:document instead. + line: 4 + column: 1 + suggestions: null +- message: Special element element is deprecated in v5, use svelte:element instead. + line: 5 + column: 1 + suggestions: null +- message: Special options element is deprecated in v5, use svelte:options instead. + line: 6 + column: 1 + suggestions: null diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-deprecated-raw-special-elements/invalid/test01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-deprecated-raw-special-elements/invalid/test01-input.svelte new file mode 100644 index 000000000..0856e15ef --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-deprecated-raw-special-elements/invalid/test01-input.svelte @@ -0,0 +1,6 @@ + + + + + + diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-deprecated-raw-special-elements/invalid/test01-output.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-deprecated-raw-special-elements/invalid/test01-output.svelte new file mode 100644 index 000000000..507eea7d8 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-deprecated-raw-special-elements/invalid/test01-output.svelte @@ -0,0 +1,6 @@ + + + + + + diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-deprecated-raw-special-elements/valid/test01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-deprecated-raw-special-elements/valid/test01-input.svelte new file mode 100644 index 000000000..ea0dfc2a0 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-deprecated-raw-special-elements/valid/test01-input.svelte @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/packages/eslint-plugin-svelte/tests/src/rules/no-deprecated-raw-special-elements.ts b/packages/eslint-plugin-svelte/tests/src/rules/no-deprecated-raw-special-elements.ts new file mode 100644 index 000000000..dbe2fe152 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/src/rules/no-deprecated-raw-special-elements.ts @@ -0,0 +1,12 @@ +import { RuleTester } from '../../utils/eslint-compat'; +import rule from '../../../src/rules/no-deprecated-raw-special-elements'; +import { loadTestCases } from '../../utils/utils'; + +const tester = new RuleTester({ + languageOptions: { + ecmaVersion: 2020, + sourceType: 'module' + } +}); + +tester.run('no-deprecated-raw-special-elements', rule as any, loadTestCases('no-deprecated-raw-special-elements'));