Skip to content

Commit 04fc429

Browse files
authored
feat!: updated the html-self-closing rule to follow Svelte5 (#982)
1 parent eae0e2e commit 04fc429

26 files changed

+142
-57
lines changed

.changeset/soft-bears-hear.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'eslint-plugin-svelte': major
3+
---
4+
5+
feat!: Updated the `html-self-closing` rule to follow Svelte5

docs/rules/html-self-closing.md

+35-21
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ since: 'v2.5.0'
1414

1515
## :book: Rule Details
1616

17-
You can choose either two styles for elements without content
17+
You can choose either two styles for elements without content.
1818

19-
- always: `<div />`
20-
- never: `<div></div>`
19+
- always: `<SomeComponent />`
20+
- never: `<SomeComponent></SomeComponent>`
2121

2222
<!-- prettier-ignore-start -->
2323
<!--eslint-skip-->
@@ -28,18 +28,21 @@ You can choose either two styles for elements without content
2828
</script>
2929
3030
<!-- ✓ GOOD -->
31-
<div />
3231
<p>Hello</p>
33-
<div><div /></div>
32+
<div></div>
3433
<img />
3534
<svelte:head />
35+
<svg><path /></svg>
36+
<math><msup></msup></math>
37+
<SomeComponent />
3638
3739
<!-- ✗ BAD -->
38-
<div></div>
39-
<p> </p>
40-
<div><div></div></div>
41-
<img>
40+
<div />
41+
<div><div /></div>
4242
<svelte:body></svelte:body>
43+
<svg><path></path></svg>
44+
<math><msup /></math>
45+
<SomeComponent></SomeComponent>
4346
```
4447

4548
<!-- prettier-ignore-end -->
@@ -52,7 +55,7 @@ presets:
5255
{
5356
"svelte/html-self-closing": [
5457
"error",
55-
"all" // or "html" or "none"
58+
"default" // or "all" or "html" or "none"
5659
]
5760
}
5861
```
@@ -65,8 +68,9 @@ config object:
6568
"error",
6669
{
6770
"void": "always", // or "never" or "ignore"
68-
"normal": "always", // or "never" or "ignore"
69-
"foreign": "always", // or "never" or "ignore"
71+
"normal": "never", // or "always" or "ignore"
72+
"svg": "always", // or "never" or "ignore"
73+
"never": "never", // or "always" or "ignore"
7074
"component": "always", // or "never" or "ignore"
7175
"svelte": "always" // or "never" or "ignore"
7276
}
@@ -76,23 +80,33 @@ config object:
7680

7781
presets:
7882

79-
- `all` - all elements should be self closing (unless they have children)
80-
- `html` - html-compliant - only void elements and svelte special elements should be self closing
81-
- `none` - no elements should be self closing
83+
- `default` - MathML and non-void HTML elements should have a closing tag; otherwise, they should be self-closing.
84+
- `all` - all elements should be self-closing (unless they have children)
85+
- `html` - html-compliant - only void elements and svelte special elements should be self-closing
86+
- `none` - no elements should be self-closing
87+
88+
::: warning Note
89+
We recommend selecting `default` as the preset. Choosing any other option may result in settings that are inconsistent with the compiler when using Svelte5.
90+
:::
8291

8392
config object:
8493

8594
- `void` (`"always"` in default preset)... Style of HTML void elements
86-
- `foreign` (`"always"` in default preset)... Style of foreign elements (SVG and MathML)
95+
- `normal` (`"never"` in default preset)... Style of other elements
96+
- `svg` (`"always"` in default preset)... Style of SVG
97+
- `math` (`never` in default preset)... Style of MathML
8798
- `component` (`"always"` in default preset)... Style of svelte components
8899
- `svelte` (`"always"` in default preset)... Style of svelte special elements (`<svelte:head>`, `<svelte:self>`)
89-
- `normal` (`"always"` in default preset)... Style of other elements
90100

91-
Every config oject option can be set to
101+
::: warning
102+
`foreign` is removed in `eslint-plugin-svelte` v3.
103+
:::
104+
105+
Every config object option can be set to
92106

93-
- "always" (`<div />`)
94-
- "never" (`<div></div>`)
95-
- "ignore" (either `<div />` or `<div></div>`)
107+
- "always" (`<SomeComponent />`)
108+
- "never" (`<SomeComponent></SomeComponent>`)
109+
- "ignore" (either `<SomeComponent />` or `<SomeComponent></SomeComponent>`)
96110

97111
## :rocket: Version
98112

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -419,7 +419,8 @@ type SvelteHtmlQuotes = []|[{
419419
type SvelteHtmlSelfClosing = []|[({
420420
void?: ("never" | "always" | "ignore")
421421
normal?: ("never" | "always" | "ignore")
422-
foreign?: ("never" | "always" | "ignore")
422+
svg?: ("never" | "always" | "ignore")
423+
math?: ("never" | "always" | "ignore")
423424
component?: ("never" | "always" | "ignore")
424425
svelte?: ("never" | "always" | "ignore")
425426
} | ("all" | "html" | "none"))]

packages/eslint-plugin-svelte/src/rules/html-self-closing.ts

+39-14
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,23 @@
11
import type { AST } from 'svelte-eslint-parser';
22
import { createRule } from '../utils/index.js';
3-
import { getNodeName, isVoidHtmlElement, isForeignElement } from '../utils/ast-utils.js';
3+
import {
4+
getNodeName,
5+
isVoidHtmlElement,
6+
isSvgElement,
7+
isMathMLElement
8+
} from '../utils/ast-utils.js';
49
import { getSourceCode } from '../utils/compat.js';
510

611
const TYPE_MESSAGES = {
712
normal: 'HTML elements',
813
void: 'HTML void elements',
9-
foreign: 'foreign (SVG or MathML) elements',
14+
svg: 'SVG elements',
15+
math: 'MathML elements',
1016
component: 'Svelte custom components',
1117
svelte: 'Svelte special elements'
1218
};
1319

14-
type ElementTypes = 'normal' | 'void' | 'foreign' | 'component' | 'svelte';
20+
type ElementTypes = 'normal' | 'void' | 'svg' | 'math' | 'component' | 'svelte';
1521

1622
export default createRule('html-self-closing', {
1723
meta: {
@@ -38,7 +44,10 @@ export default createRule('html-self-closing', {
3844
normal: {
3945
enum: ['never', 'always', 'ignore']
4046
},
41-
foreign: {
47+
svg: {
48+
enum: ['never', 'always', 'ignore']
49+
},
50+
math: {
4251
enum: ['never', 'always', 'ignore']
4352
},
4453
component: {
@@ -58,34 +67,49 @@ export default createRule('html-self-closing', {
5867
]
5968
},
6069
create(context) {
70+
// default
6171
let options = {
6272
void: 'always',
63-
normal: 'always',
64-
foreign: 'always',
73+
normal: 'never',
74+
svg: 'always',
75+
math: 'never',
6576
component: 'always',
6677
svelte: 'always'
6778
};
6879

6980
const option = context.options?.[0];
7081
switch (option) {
71-
case 'none':
82+
case 'all':
7283
options = {
73-
void: 'never',
74-
normal: 'never',
75-
foreign: 'never',
76-
component: 'never',
77-
svelte: 'never'
84+
void: 'always',
85+
normal: 'always',
86+
svg: 'always',
87+
math: 'always',
88+
component: 'always',
89+
svelte: 'always'
7890
};
7991
break;
8092
case 'html':
8193
options = {
8294
void: 'always',
8395
normal: 'never',
84-
foreign: 'always',
96+
svg: 'always',
97+
math: 'never',
8598
component: 'never',
8699
svelte: 'always'
87100
};
88101
break;
102+
case 'none':
103+
options = {
104+
void: 'never',
105+
normal: 'never',
106+
svg: 'never',
107+
math: 'never',
108+
component: 'never',
109+
svelte: 'never'
110+
};
111+
break;
112+
89113
default:
90114
if (typeof option !== 'object' || option === null) break;
91115

@@ -108,7 +132,8 @@ export default createRule('html-self-closing', {
108132
if (node.kind === 'component') return 'component';
109133
if (node.kind === 'special') return 'svelte';
110134
if (isVoidHtmlElement(node)) return 'void';
111-
if (isForeignElement(node)) return 'foreign';
135+
if (isSvgElement(node)) return 'svg';
136+
if (isMathMLElement(node)) return 'math';
112137
return 'normal';
113138
}
114139

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

+8
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,14 @@ export function isForeignElement(node: SvAST.SvelteElement): boolean {
567567
return svgElements.includes(getNodeName(node)) || mathmlElements.includes(getNodeName(node));
568568
}
569569

570+
export function isSvgElement(node: SvAST.SvelteElement): boolean {
571+
return svgElements.includes(getNodeName(node));
572+
}
573+
574+
export function isMathMLElement(node: SvAST.SvelteElement): boolean {
575+
return mathmlElements.includes(getNodeName(node));
576+
}
577+
570578
/** Checks whether the given identifier node is used as an expression. */
571579
export function isExpressionIdentifier(node: TSESTree.Identifier): boolean {
572580
const parent = node.parent;

packages/eslint-plugin-svelte/tests/fixtures/rules/html-self-closing/invalid/foreign-never/svelte-never-errors.yaml

-8
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"options": [
33
{
4-
"foreign": "never"
4+
"math": "never"
55
}
66
]
77
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
- message: Disallow self-closing on MathML elements.
2+
line: 3
3+
column: 13
4+
suggestions: null
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
<!-- prettier-ignore -->
2-
<svg><path ></path></svg>
2+
<svg><path /></svg>
33
<math><msup ></msup></math>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"options": [
3+
{
4+
"normal": "always"
5+
}
6+
]
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
- message: Require self-closing on HTML elements.
2+
line: 3
3+
column: 7
4+
suggestions: null
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<!-- prettier-ignore -->
2+
<div>
3+
<div></div>
4+
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<!-- prettier-ignore -->
2+
<div>
3+
<div/>
4+
</div>

packages/eslint-plugin-svelte/tests/fixtures/rules/html-self-closing/invalid/presets/html/preset-html-errors.yaml

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@
1010
line: 5
1111
column: 18
1212
suggestions: null
13-
- message: Require self-closing on foreign (SVG or MathML) elements.
13+
- message: Require self-closing on SVG elements.
1414
line: 6
1515
column: 13
1616
suggestions: null
17-
- message: Require self-closing on foreign (SVG or MathML) elements.
17+
- message: Disallow self-closing on MathML elements.
1818
line: 7
1919
column: 14
2020
suggestions: null

packages/eslint-plugin-svelte/tests/fixtures/rules/html-self-closing/invalid/presets/html/preset-html-input.svelte

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<img>
55
<TestComponent />
66
<svg><path></path></svg>
7-
<math><msup></msup></math>
7+
<math><msup/></math>
88
</div>
99
<!-- prettier-ignore -->
1010
<svelte:head></svelte:head>

packages/eslint-plugin-svelte/tests/fixtures/rules/html-self-closing/invalid/presets/html/preset-html-output.svelte

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<img/>
55
<TestComponent ></TestComponent>
66
<svg><path/></svg>
7-
<math><msup/></math>
7+
<math><msup></msup></math>
88
</div>
99
<!-- prettier-ignore -->
1010
<svelte:head/>

packages/eslint-plugin-svelte/tests/fixtures/rules/html-self-closing/invalid/presets/none/preset-none-errors.yaml

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@
1010
line: 5
1111
column: 8
1212
suggestions: null
13-
- message: Disallow self-closing on foreign (SVG or MathML) elements.
13+
- message: Disallow self-closing on SVG elements.
1414
line: 6
1515
column: 14
1616
suggestions: null
17-
- message: Disallow self-closing on foreign (SVG or MathML) elements.
17+
- message: Disallow self-closing on MathML elements.
1818
line: 7
1919
column: 15
2020
suggestions: null
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"options": [
3+
{
4+
"svg": "never"
5+
}
6+
]
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
- message: Disallow self-closing on SVG elements.
2+
line: 2
3+
column: 12
4+
suggestions: null
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<!-- prettier-ignore -->
2+
<svg><path /></svg>
3+
<math><msup></msup></math>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<!-- prettier-ignore -->
2+
<svg><path ></path></svg>
3+
<math><msup></msup></math>

packages/eslint-plugin-svelte/tests/fixtures/rules/html-self-closing/invalid/test01-errors.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
- message: Require self-closing on HTML elements.
1+
- message: Disallow self-closing on HTML elements.
22
line: 3
33
column: 7
44
suggestions: null
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<!-- prettier-ignore -->
22
<div>
3-
<div></div>
3+
<div/>
44
<CustomElement> </CustomElement>
55
<img>
66
</div>
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<!-- prettier-ignore -->
22
<div>
3-
<div/>
3+
<div></div>
44
<CustomElement/>
55
<img/>
66
</div>

packages/eslint-plugin-svelte/tests/fixtures/rules/html-self-closing/valid/test01-input.svelte

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
<div class="hello">
2-
<div />
2+
<div></div>
33
<div>hello</div>
44
<img />
55
<svg><path /></svg>
6-
<math><msup /></math>
6+
<math><msup></msup></math>
77
{#if true}
88
<svelte:self />
99
{/if}

0 commit comments

Comments
 (0)