Skip to content

Commit a74299e

Browse files
committed
Merge branch 'main' of github.com:ota-meshi/eslint-plugin-svelte
2 parents 9c9394c + fcb5e31 commit a74299e

15 files changed

+223
-8
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)

docs/rules/experimental-require-strict-events.md

+3-6
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,9 @@ This rule enforces the presence of the `strictEvents` attribute on the main `<sc
2020
<!--eslint-skip-->
2121

2222
```svelte
23-
<!-- eslint svelte/experimental-require-strict-events: "error" -->
24-
2523
<!-- ✓ GOOD -->
2624
<script lang="ts" strictEvents>
25+
/* eslint svelte/experimental-require-strict-events: "error" */
2726
</script>
2827
```
2928

@@ -34,10 +33,9 @@ This rule enforces the presence of the `strictEvents` attribute on the main `<sc
3433
<!--eslint-skip-->
3534

3635
```svelte
37-
<!-- eslint svelte/experimental-require-strict-events: "error" -->
38-
3936
<!-- ✓ GOOD -->
4037
<script lang="ts">
38+
/* eslint svelte/experimental-require-strict-events: "error" */
4139
interface $$Events {}
4240
</script>
4341
```
@@ -49,10 +47,9 @@ This rule enforces the presence of the `strictEvents` attribute on the main `<sc
4947
<!--eslint-skip-->
5048

5149
```svelte
52-
<!-- eslint svelte/experimental-require-strict-events: "error" -->
53-
5450
<!-- ✗ BAD -->
5551
<script lang="ts">
52+
/* eslint svelte/experimental-require-strict-events: "error" */
5653
</script>
5754
```
5855

package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,8 @@
157157
"sass": "^1.51.0",
158158
"semver": "^7.3.5",
159159
"simple-git-hooks": "^2.8.0",
160-
"stylelint": "^14.0.0",
161-
"stylelint-config-standard": "^29.0.0",
160+
"stylelint": "^15.0.0",
161+
"stylelint-config-standard": "^30.0.0",
162162
"stylus": "^0.59.0",
163163
"svelte": "^3.46.1",
164164
"svelte-adapter-ghpages": "0.1.0",
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)