Skip to content

Commit fcb5e31

Browse files
feat: Added the experimental-require-slot-types rule (#368)
Co-authored-by: ota-meshi <otameshiyo23@gmail.com>
1 parent 5c41254 commit fcb5e31

13 files changed

+218
-0
lines changed

.changeset/strong-wombats-worry.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"eslint-plugin-svelte": minor
3+
---
4+
5+
feat: added the `svelte/experimental-require-slot-types` rule

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,7 @@ These rules extend the rules provided by ESLint itself, or other plugins to work
379379

380380
| Rule ID | Description | |
381381
|:--------|:------------|:---|
382+
| [svelte/experimental-require-slot-types](https://ota-meshi.github.io/eslint-plugin-svelte/rules/experimental-require-slot-types/) | require slot type declaration using the `$$Slots` interface | |
382383
| [svelte/experimental-require-strict-events](https://ota-meshi.github.io/eslint-plugin-svelte/rules/experimental-require-strict-events/) | require the strictEvents attribute on `<script>` tags | |
383384

384385
## System

docs/rules.md

+1
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ These rules extend the rules provided by ESLint itself, or other plugins to work
9898

9999
| Rule ID | Description | |
100100
|:--------|:------------|:---|
101+
| [svelte/experimental-require-slot-types](./rules/experimental-require-slot-types.md) | require slot type declaration using the `$$Slots` interface | |
101102
| [svelte/experimental-require-strict-events](./rules/experimental-require-strict-events.md) | require the strictEvents attribute on `<script>` tags | |
102103

103104
## System
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
---
2+
pageClass: "rule-details"
3+
sidebarDepth: 0
4+
title: "svelte/experimental-require-slot-types"
5+
description: "require slot type declaration using the `$$Slots` interface"
6+
---
7+
8+
# svelte/experimental-require-slot-types
9+
10+
> require slot type declaration using the `$$Slots` interface
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+
14+
## :book: Rule Details
15+
16+
This rule enforces the presence of the `$$Slots` interface if any slots are present in the component. This interface declares all of the used slots and their props and enables typechecking both in the component itself as well as all components that include it.
17+
The `$$Slots` interface is experimental and is documented in [svelte RFC #38](https://github.com/dummdidumm/rfcs/blob/ts-typedefs-within-svelte-components/text/ts-typing-props-slots-events.md#typing-slots).
18+
19+
<ESLintCodeBlock>
20+
21+
<!--eslint-skip-->
22+
23+
```svelte
24+
<!-- ✓ GOOD -->
25+
<script>
26+
/* eslint svelte/experimental-require-slot-types: "error" */
27+
</script>
28+
29+
<b>No slots here!</b>
30+
```
31+
32+
</ESLintCodeBlock>
33+
34+
<ESLintCodeBlock>
35+
36+
<!--eslint-skip-->
37+
38+
```svelte
39+
<!-- ✓ GOOD -->
40+
<script>
41+
/* eslint svelte/experimental-require-slot-types: "error" */
42+
43+
interface $$Slots {
44+
default: Record<string, never>;
45+
}
46+
</script>
47+
48+
<slot />
49+
```
50+
51+
</ESLintCodeBlock>
52+
53+
<ESLintCodeBlock>
54+
55+
<!--eslint-skip-->
56+
57+
```svelte
58+
<!-- ✓ GOOD -->
59+
<script lang="ts">
60+
/* eslint svelte/experimental-require-slot-types: "error" */
61+
62+
interface $$Slots {
63+
default: { prop: boolean; };
64+
}
65+
</script>
66+
67+
<slot prop={true} />
68+
```
69+
70+
</ESLintCodeBlock>
71+
72+
<ESLintCodeBlock>
73+
74+
<!--eslint-skip-->
75+
76+
```svelte
77+
<!-- ✓ GOOD -->
78+
<script lang="ts">
79+
/* eslint svelte/experimental-require-slot-types: "error" */
80+
81+
interface $$Slots {
82+
named: Record<string, never>;
83+
}
84+
</script>
85+
86+
<slot name = "named" />
87+
```
88+
89+
</ESLintCodeBlock>
90+
91+
<ESLintCodeBlock>
92+
93+
<!--eslint-skip-->
94+
95+
```svelte
96+
<!-- ✗ BAD -->
97+
<script>
98+
/* eslint svelte/experimental-require-slot-types: "error" */
99+
</script>
100+
101+
<slot />
102+
```
103+
104+
</ESLintCodeBlock>
105+
106+
## :wrench: Options
107+
108+
Nothing.
109+
110+
## :mag: Implementation
111+
112+
- [Rule source](https://github.com/ota-meshi/eslint-plugin-svelte/blob/main/src/rules/experimental-require-slot-types.ts)
113+
- [Test source](https://github.com/ota-meshi/eslint-plugin-svelte/blob/main/tests/src/rules/experimental-require-slot-types.ts)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { createRule } from "../utils"
2+
import { getLangValue } from "../utils/ast-utils"
3+
4+
export default createRule("experimental-require-slot-types", {
5+
meta: {
6+
docs: {
7+
description:
8+
"require slot type declaration using the `$$Slots` interface",
9+
category: "Experimental",
10+
recommended: false,
11+
},
12+
schema: [],
13+
messages: {
14+
missingSlotsInterface: `The component must define the $$Slots interface.`,
15+
},
16+
type: "suggestion",
17+
},
18+
create(context) {
19+
let isTs = false
20+
let hasSlot = false
21+
let hasInterface = false
22+
return {
23+
SvelteScriptElement(node) {
24+
const lang = getLangValue(node)?.toLowerCase()
25+
isTs = lang === "ts" || lang === "typescript"
26+
},
27+
SvelteElement(node) {
28+
if (node.name.type === "SvelteName" && node.name.name === "slot") {
29+
hasSlot = true
30+
}
31+
},
32+
TSInterfaceDeclaration(node) {
33+
if (node.id.name === "$$Slots") {
34+
hasInterface = true
35+
}
36+
},
37+
"Program:exit"() {
38+
if (isTs && hasSlot && !hasInterface) {
39+
context.report({
40+
loc: {
41+
line: 1,
42+
column: 1,
43+
},
44+
messageId: "missingSlotsInterface",
45+
})
46+
}
47+
},
48+
}
49+
},
50+
})

src/utils/rules.ts

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import typescriptEslintNoUnnecessaryCondition from "../rules/@typescript-eslint/
33
import buttonHasType from "../rules/button-has-type"
44
import commentDirective from "../rules/comment-directive"
55
import derivedHasSameInputsOutputs from "../rules/derived-has-same-inputs-outputs"
6+
import experimentalRequireSlotTypes from "../rules/experimental-require-slot-types"
67
import experimentalRequireStrictEvents from "../rules/experimental-require-strict-events"
78
import firstAttributeLinebreak from "../rules/first-attribute-linebreak"
89
import htmlClosingBracketSpacing from "../rules/html-closing-bracket-spacing"
@@ -56,6 +57,7 @@ export const rules = [
5657
buttonHasType,
5758
commentDirective,
5859
derivedHasSameInputsOutputs,
60+
experimentalRequireSlotTypes,
5961
experimentalRequireStrictEvents,
6062
firstAttributeLinebreak,
6163
htmlClosingBracketSpacing,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
- message: The component must define the $$Slots interface.
2+
line: 1
3+
column: 2
4+
suggestions: null
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<script lang="ts">
2+
</script>
3+
4+
<slot />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<script lang="ts">
2+
interface $$Slots {
3+
defalt: Record<string, never>
4+
}
5+
</script>
6+
7+
<slot />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<script lang="ts">
2+
interface $$Slots {
3+
named: Record<string, never>
4+
}
5+
</script>
6+
7+
<slot name="named" />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<script lang="ts">
2+
</script>
3+
4+
content
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<script>
2+
</script>
3+
4+
<slot />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { RuleTester } from "eslint"
2+
import rule from "../../../src/rules/experimental-require-slot-types"
3+
import { loadTestCases } from "../../utils/utils"
4+
5+
const tester = new RuleTester({
6+
parserOptions: {
7+
ecmaVersion: 2020,
8+
sourceType: "module",
9+
},
10+
})
11+
12+
tester.run(
13+
"experimental-require-slot-types",
14+
rule as any,
15+
loadTestCases("experimental-require-slot-types"),
16+
)

0 commit comments

Comments
 (0)