Skip to content

Commit

Permalink
feat: add rule no-deprecated-raw-special-elements (#918)
Browse files Browse the repository at this point in the history
~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]>
  • Loading branch information
mikededo and ota-meshi authored Nov 27, 2024
1 parent f8dd94d commit 5da98c9
Show file tree
Hide file tree
Showing 12 changed files with 169 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .changeset/lazy-eyes-wait.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'eslint-plugin-svelte': minor
---

Added new `no-deprecated-raw-special-elements` rule
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 | |
Expand Down
1 change: 1 addition & 0 deletions docs/rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 | |
Expand Down
52 changes: 52 additions & 0 deletions docs/rules/no-deprecated-raw-special-elements.md
Original file line number Diff line number Diff line change
@@ -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: <badge text="This rule has not been released yet." vertical="middle" type="error"> **_This rule has not been released yet._** </badge>
- :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:`.

<ESLintCodeBlock fix>

<!--eslint-skip-->

```svelte
<script>
/* eslint svelte/no-deprecated-raw-special-elements: "error" */
</script>
<!-- ✓ GOOD -->
<svelte:head>
<title>Valid</title>
</svelte:head>
<!-- ✗ BAD -->
<head>
<title>Invalid</title>
</head>
```

</ESLintCodeBlock>

## :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)
5 changes: 5 additions & 0 deletions packages/eslint-plugin-svelte/src/rule-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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/
Expand Down
Original file line number Diff line number Diff line change
@@ -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

Check warning on line 12 in packages/eslint-plugin-svelte/src/rules/no-deprecated-raw-special-elements.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected 'todo' comment: 'TODO: Switch to recommended in the major...'
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
);
}
}
});
}
}
};
}
});
2 changes: 2 additions & 0 deletions packages/eslint-plugin-svelte/src/utils/rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -87,6 +88,7 @@ export const rules = [
mustacheSpacing,
noAtDebugTags,
noAtHtmlTags,
noDeprecatedRawSpecialElements,
noDomManipulating,
noDupeElseIfBlocks,
noDupeOnDirectives,
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<head></head>
<body></body>
<window></window>
<document></document>
<element this={{}}></element>
<options></options>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<svelte:head></svelte:head>
<svelte:body></svelte:body>
<svelte:window></svelte:window>
<svelte:document></svelte:document>
<svelte:element this={{}}></svelte:element>
<svelte:options></svelte:options>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<svelte:options />

<svelte:body />
<svelte:document />
<svelte:element this={{}}></svelte:element>
<svelte:head></svelte:head>

<svelte:window />
Original file line number Diff line number Diff line change
@@ -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'));

0 comments on commit 5da98c9

Please sign in to comment.