Skip to content

Commit e61bbc3

Browse files
authored
feat: Added the experimental-require-strict-events rule (#365)
1 parent 15ee049 commit e61bbc3

15 files changed

+156
-0
lines changed

.changeset/tasty-houses-behave.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"eslint-plugin-svelte": minor
3+
---
4+
5+
Added the experimental-require-strict-events rule

README.md

+8
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,14 @@ These rules extend the rules provided by ESLint itself, or other plugins to work
373373
| [svelte/no-inner-declarations](https://ota-meshi.github.io/eslint-plugin-svelte/rules/no-inner-declarations/) | disallow variable or `function` declarations in nested blocks | :star: |
374374
| [svelte/no-trailing-spaces](https://ota-meshi.github.io/eslint-plugin-svelte/rules/no-trailing-spaces/) | disallow trailing whitespace at the end of lines | :wrench: |
375375

376+
## Experimental
377+
378+
:warning: These rules are considered experimental and may change or be removed in the future:
379+
380+
| Rule ID | Description | |
381+
|:--------|:------------|:---|
382+
| [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 | |
383+
376384
## System
377385

378386
These rules relate to this plugin works:

docs/rules.md

+8
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,14 @@ These rules extend the rules provided by ESLint itself, or other plugins to work
9292
| [svelte/no-inner-declarations](./rules/no-inner-declarations.md) | disallow variable or `function` declarations in nested blocks | :star: |
9393
| [svelte/no-trailing-spaces](./rules/no-trailing-spaces.md) | disallow trailing whitespace at the end of lines | :wrench: |
9494

95+
## Experimental
96+
97+
:warning: These rules are considered experimental and may change or be removed in the future:
98+
99+
| Rule ID | Description | |
100+
|:--------|:------------|:---|
101+
| [svelte/experimental-require-strict-events](./rules/experimental-require-strict-events.md) | require the strictEvents attribute on <script> tags | |
102+
95103
## System
96104

97105
These rules relate to this plugin works:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
---
2+
pageClass: "rule-details"
3+
sidebarDepth: 0
4+
title: "svelte/experimental-require-strict-events"
5+
description: "require the strictEvents attribute on <script> tags"
6+
---
7+
8+
# svelte/experimental-require-strict-events
9+
10+
> require the strictEvents attribute on <script> tags
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 `strictEvents` attribute on the main `<script>` tag of all components. This attributes enforces typechecking of events dispatched by the component, e.g. making it a typescript error to listen to any non-existent events. Alternatively, the event types may be defined manually by declaring the `$$Events` interface. The `strictEvents` attribute and the `$$Events` interface are experimental and are documented in [svelte RFC #38](https://github.com/dummdidumm/rfcs/blob/ts-typedefs-within-svelte-components/text/ts-typing-props-slots-events.md#typing-events).
17+
18+
<ESLintCodeBlock>
19+
20+
<!--eslint-skip-->
21+
22+
```svelte
23+
<!-- eslint svelte/experimental-require-strict-events: "error" -->
24+
25+
<!-- ✓ GOOD -->
26+
<script lang="ts" strictEvents>
27+
</script>
28+
29+
<script lang="ts">
30+
interface $$Events {}
31+
</script>
32+
33+
<!-- ✗ BAD -->
34+
<script lang="ts">
35+
</script>
36+
```
37+
38+
</ESLintCodeBlock>
39+
40+
## :wrench: Options
41+
42+
Nothing.
43+
44+
## :mag: Implementation
45+
46+
- [Rule source](https://github.com/ota-meshi/eslint-plugin-svelte/blob/main/src/rules/experimental-require-strict-events.ts)
47+
- [Test source](https://github.com/ota-meshi/eslint-plugin-svelte/blob/main/tests/src/rules/experimental-require-strict-events.ts)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import type { AST } from "svelte-eslint-parser"
2+
3+
import { createRule } from "../utils"
4+
import { findAttribute, getLangValue } from "../utils/ast-utils"
5+
6+
export default createRule("experimental-require-strict-events", {
7+
meta: {
8+
docs: {
9+
description: "require the strictEvents attribute on <script> tags",
10+
category: "Experimental",
11+
recommended: false,
12+
},
13+
schema: [],
14+
messages: {
15+
missingStrictEvents: `The component must have the strictEvents attribute on its <script> tag or it must define the $$Events interface.`,
16+
},
17+
type: "suggestion",
18+
},
19+
create(context) {
20+
let isTs = false
21+
let hasAttribute = false
22+
let hasInterface = false
23+
let scriptNode: AST.SvelteScriptElement
24+
return {
25+
SvelteScriptElement(node) {
26+
const lang = getLangValue(node)?.toLowerCase()
27+
isTs = lang === "ts" || lang === "typescript"
28+
hasAttribute = findAttribute(node, "strictEvents") !== null
29+
scriptNode = node
30+
},
31+
TSInterfaceDeclaration(node) {
32+
if (node.id.name === "$$Events") {
33+
hasInterface = true
34+
}
35+
},
36+
"Program:exit"() {
37+
if (isTs && !hasAttribute && !hasInterface) {
38+
context.report({
39+
node: scriptNode,
40+
messageId: "missingStrictEvents",
41+
})
42+
}
43+
},
44+
}
45+
},
46+
})

src/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ export type RuleCategory =
5353
| "Best Practices"
5454
| "Stylistic Issues"
5555
| "Extension Rules"
56+
| "Experimental"
5657
| "System"
5758

5859
export interface RuleMetaData {

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 experimentalRequireStrictEvents from "../rules/experimental-require-strict-events"
67
import firstAttributeLinebreak from "../rules/first-attribute-linebreak"
78
import htmlClosingBracketSpacing from "../rules/html-closing-bracket-spacing"
89
import htmlQuotes from "../rules/html-quotes"
@@ -55,6 +56,7 @@ export const rules = [
5556
buttonHasType,
5657
commentDirective,
5758
derivedHasSameInputsOutputs,
59+
experimentalRequireStrictEvents,
5860
firstAttributeLinebreak,
5961
htmlClosingBracketSpacing,
6062
htmlQuotes,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
- message:
2+
The component must have the strictEvents attribute on its <script> tag
3+
or it must define the $$Events interface.
4+
line: 1
5+
column: 1
6+
suggestions: null
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<script lang="ts">
2+
</script>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<script lang="ts">
2+
interface $$Events {}
3+
</script>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<script lang="ts" strictEvents>
2+
</script>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<script>
2+
</script>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<script lang="ts" context="module">
2+
</script>
3+
4+
<script lang="ts" strictEvents>
5+
</script>
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-strict-events"
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-strict-events",
14+
rule as any,
15+
loadTestCases("experimental-require-strict-events"),
16+
)

tools/render-rules.ts

+3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const categories = [
77
"Best Practices",
88
"Stylistic Issues",
99
"Extension Rules",
10+
"Experimental",
1011
"System",
1112
] as const
1213

@@ -21,6 +22,8 @@ const descriptions: Record<(typeof categories)[number], string> = {
2122
"These rules relate to style guidelines, and are therefore quite subjective:",
2223
"Extension Rules":
2324
"These rules extend the rules provided by ESLint itself, or other plugins to work well in Svelte:",
25+
Experimental:
26+
":warning: These rules are considered experimental and may change or be removed in the future:",
2427
System: "These rules relate to this plugin works:",
2528
}
2629

0 commit comments

Comments
 (0)