Skip to content

Commit 5da98c9

Browse files
mikededoota-meshi
andauthored
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 <[email protected]>
1 parent f8dd94d commit 5da98c9

File tree

12 files changed

+169
-0
lines changed

12 files changed

+169
-0
lines changed

.changeset/lazy-eyes-wait.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'eslint-plugin-svelte': minor
3+
---
4+
5+
Added new `no-deprecated-raw-special-elements` rule

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,7 @@ These rules relate to possible syntax or logic errors in Svelte code:
380380
| Rule ID | Description | |
381381
|:--------|:------------|:---|
382382
| [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. | |
383+
| [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: |
383384
| [svelte/no-dom-manipulating](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-dom-manipulating/) | disallow DOM manipulating | |
384385
| [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: |
385386
| [svelte/no-dupe-on-directives](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-dupe-on-directives/) | disallow duplicate `on:` directives | |

docs/rules.md

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ These rules relate to possible syntax or logic errors in Svelte code:
1717
| Rule ID | Description | |
1818
| :------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------- | :------------- |
1919
| [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. | |
20+
| [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: |
2021
| [svelte/no-dom-manipulating](./rules/no-dom-manipulating.md) | disallow DOM manipulating | |
2122
| [svelte/no-dupe-else-if-blocks](./rules/no-dupe-else-if-blocks.md) | disallow duplicate conditions in `{#if}` / `{:else if}` chains | :star: |
2223
| [svelte/no-dupe-on-directives](./rules/no-dupe-on-directives.md) | disallow duplicate `on:` directives | |
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
---
2+
pageClass: 'rule-details'
3+
sidebarDepth: 0
4+
title: 'svelte/no-deprecated-raw-special-elements'
5+
description: 'Recommends not using raw special elements in Svelte versions previous to 5.'
6+
---
7+
8+
# svelte/no-deprecated-raw-special-elements
9+
10+
> Recommends not using raw special elements in Svelte versions previous to 5.
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+
- :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.
14+
15+
## :book: Rule Details
16+
17+
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:`.
18+
19+
<ESLintCodeBlock fix>
20+
21+
<!--eslint-skip-->
22+
23+
```svelte
24+
<script>
25+
/* eslint svelte/no-deprecated-raw-special-elements: "error" */
26+
</script>
27+
28+
<!-- ✓ GOOD -->
29+
<svelte:head>
30+
<title>Valid</title>
31+
</svelte:head>
32+
33+
<!-- ✗ BAD -->
34+
<head>
35+
<title>Invalid</title>
36+
</head>
37+
```
38+
39+
</ESLintCodeBlock>
40+
41+
## :wrench: Options
42+
43+
Nothing.
44+
45+
## :books: Further Reading
46+
47+
- See special elements section in [Svelte docs](https://svelte.dev/docs/svelte/svelte-window)
48+
49+
## :mag: Implementation
50+
51+
- [Rule source](https://github.com/sveltejs/eslint-plugin-svelte/blob/main/packages/eslint-plugin-svelte/src/rules/no-deprecated-raw-special-elements.ts)
52+
- [Test source](https://github.com/sveltejs/eslint-plugin-svelte/blob/main/packages/eslint-plugin-svelte/tests/src/rules/no-deprecated-raw-special-elements.ts)

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

+5
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,11 @@ export interface RuleOptions {
104104
* @see https://sveltejs.github.io/eslint-plugin-svelte/rules/no-at-html-tags/
105105
*/
106106
'svelte/no-at-html-tags'?: Linter.RuleEntry<[]>
107+
/**
108+
* Recommends not using raw special elements in Svelte versions previous to 5.
109+
* @see https://sveltejs.github.io/eslint-plugin-svelte/rules/no-deprecated-raw-special-elements/
110+
*/
111+
'svelte/no-deprecated-raw-special-elements'?: Linter.RuleEntry<[]>
107112
/**
108113
* disallow DOM manipulating
109114
* @see https://sveltejs.github.io/eslint-plugin-svelte/rules/no-dom-manipulating/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import type { AST } from 'svelte-eslint-parser';
2+
import { createRule } from '../utils';
3+
4+
const INVALID_HTML_ELEMENTS = ['head', 'body', 'window', 'document', 'element', 'options'];
5+
const VALID_PREFIX = 'svelte:';
6+
7+
export default createRule('no-deprecated-raw-special-elements', {
8+
meta: {
9+
docs: {
10+
description: 'Recommends not using raw special elements in Svelte versions previous to 5.',
11+
category: 'Possible Errors',
12+
// TODO: Switch to recommended in the major version
13+
recommended: false
14+
},
15+
schema: [],
16+
messages: {
17+
deprecatedElement:
18+
'Special {{name}} element is deprecated in v5, use svelte:{{name}} instead.'
19+
},
20+
type: 'problem', // 'problem', or 'layout',
21+
fixable: 'code'
22+
},
23+
create(context) {
24+
return {
25+
'SvelteElement[kind="html"]'(node: AST.SvelteHTMLElement) {
26+
const { name } = node.name;
27+
if (INVALID_HTML_ELEMENTS.includes(name)) {
28+
context.report({
29+
node,
30+
messageId: 'deprecatedElement',
31+
data: { name },
32+
*fix(fixer) {
33+
const { endTag } = node;
34+
yield fixer.insertTextBeforeRange([node.range[0] + 1, node.range[1]], VALID_PREFIX);
35+
if (endTag) {
36+
yield fixer.insertTextBeforeRange(
37+
[endTag.range[0] + 2, endTag.range[1]],
38+
VALID_PREFIX
39+
);
40+
}
41+
}
42+
});
43+
}
44+
}
45+
};
46+
}
47+
});

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

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import maxAttributesPerLine from '../rules/max-attributes-per-line';
2020
import mustacheSpacing from '../rules/mustache-spacing';
2121
import noAtDebugTags from '../rules/no-at-debug-tags';
2222
import noAtHtmlTags from '../rules/no-at-html-tags';
23+
import noDeprecatedRawSpecialElements from '../rules/no-deprecated-raw-special-elements';
2324
import noDomManipulating from '../rules/no-dom-manipulating';
2425
import noDupeElseIfBlocks from '../rules/no-dupe-else-if-blocks';
2526
import noDupeOnDirectives from '../rules/no-dupe-on-directives';
@@ -87,6 +88,7 @@ export const rules = [
8788
mustacheSpacing,
8889
noAtDebugTags,
8990
noAtHtmlTags,
91+
noDeprecatedRawSpecialElements,
9092
noDomManipulating,
9193
noDupeElseIfBlocks,
9294
noDupeOnDirectives,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
- message: Special head element is deprecated in v5, use svelte:head instead.
2+
line: 1
3+
column: 1
4+
suggestions: null
5+
- message: Special body element is deprecated in v5, use svelte:body instead.
6+
line: 2
7+
column: 1
8+
suggestions: null
9+
- message: Special window element is deprecated in v5, use svelte:window instead.
10+
line: 3
11+
column: 1
12+
suggestions: null
13+
- message: Special document element is deprecated in v5, use svelte:document instead.
14+
line: 4
15+
column: 1
16+
suggestions: null
17+
- message: Special element element is deprecated in v5, use svelte:element instead.
18+
line: 5
19+
column: 1
20+
suggestions: null
21+
- message: Special options element is deprecated in v5, use svelte:options instead.
22+
line: 6
23+
column: 1
24+
suggestions: null
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<head></head>
2+
<body></body>
3+
<window></window>
4+
<document></document>
5+
<element this={{}}></element>
6+
<options></options>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<svelte:head></svelte:head>
2+
<svelte:body></svelte:body>
3+
<svelte:window></svelte:window>
4+
<svelte:document></svelte:document>
5+
<svelte:element this={{}}></svelte:element>
6+
<svelte:options></svelte:options>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<svelte:options />
2+
3+
<svelte:body />
4+
<svelte:document />
5+
<svelte:element this={{}}></svelte:element>
6+
<svelte:head></svelte:head>
7+
8+
<svelte:window />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { RuleTester } from '../../utils/eslint-compat';
2+
import rule from '../../../src/rules/no-deprecated-raw-special-elements';
3+
import { loadTestCases } from '../../utils/utils';
4+
5+
const tester = new RuleTester({
6+
languageOptions: {
7+
ecmaVersion: 2020,
8+
sourceType: 'module'
9+
}
10+
});
11+
12+
tester.run('no-deprecated-raw-special-elements', rule as any, loadTestCases('no-deprecated-raw-special-elements'));

0 commit comments

Comments
 (0)