Skip to content

Commit 26870cf

Browse files
feat: add svelte/Infinite-reactive-loop rule (#332)
Co-authored-by: Yosuke Ota <[email protected]>
1 parent 088358a commit 26870cf

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+1219
-1
lines changed

.changeset/slimy-brooms-report.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"eslint-plugin-svelte": minor
3+
---
4+
5+
feat: add `svelte/Infinite-reactive-loop` rule

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,7 @@ These rules relate to possible syntax or logic errors in Svelte code:
297297

298298
| Rule ID | Description | |
299299
|:--------|:------------|:---|
300+
| [svelte/infinite-reactive-loop](https://ota-meshi.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. | |
300301
| [svelte/no-dom-manipulating](https://ota-meshi.github.io/eslint-plugin-svelte/rules/no-dom-manipulating/) | disallow DOM manipulating | |
301302
| [svelte/no-dupe-else-if-blocks](https://ota-meshi.github.io/eslint-plugin-svelte/rules/no-dupe-else-if-blocks/) | disallow duplicate conditions in `{#if}` / `{:else if}` chains | :star: |
302303
| [svelte/no-dupe-on-directives](https://ota-meshi.github.io/eslint-plugin-svelte/rules/no-dupe-on-directives/) | disallow duplicate `on:` directives | |

docs/rules.md

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ These rules relate to possible syntax or logic errors in Svelte code:
1616

1717
| Rule ID | Description | |
1818
|:--------|:------------|:---|
19+
| [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. | |
1920
| [svelte/no-dom-manipulating](./rules/no-dom-manipulating.md) | disallow DOM manipulating | |
2021
| [svelte/no-dupe-else-if-blocks](./rules/no-dupe-else-if-blocks.md) | disallow duplicate conditions in `{#if}` / `{:else if}` chains | :star: |
2122
| [svelte/no-dupe-on-directives](./rules/no-dupe-on-directives.md) | disallow duplicate `on:` directives | |

docs/rules/infinite-reactive-loop.md

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
---
2+
pageClass: "rule-details"
3+
sidebarDepth: 0
4+
title: "svelte/infinite-reactive-loop"
5+
description: "Svelte runtime prevents calling the same reactive statement twice in a microtask. But between different microtask, it doesn't prevent."
6+
---
7+
8+
# svelte/infinite-reactive-loop
9+
10+
> Svelte runtime prevents calling the same reactive statement twice in a microtask. But between different microtask, it doesn't prevent.
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+
Svelte runtime prevents calling the same reactive statement twice in a microtask.<br/>
17+
But between different microtask, it doesn't prevent.<br/>
18+
This rule reports those possible infinite loop.
19+
20+
<ESLintCodeBlock>
21+
22+
<!--eslint-skip-->
23+
24+
```svelte
25+
<script>
26+
/* eslint svelte/infinite-reactive-loop: "error" */
27+
import { count } from "./store.js"
28+
import { tick } from "svelte"
29+
let a = 0
30+
31+
// ✓ GOOD
32+
$: if (a < 10) {
33+
a += 1
34+
$count += 1
35+
}
36+
37+
$: (async () => {
38+
// You can update a state in the same micro task.
39+
a += 1
40+
$count += 1
41+
await new Promise((resolve) => setTimeout(resolve, 100))
42+
})()
43+
44+
$: (async () => {
45+
await doSomething_ok()
46+
})()
47+
48+
const doSomething_ok = async () => {
49+
await fetchFromServer()
50+
// You can update a state even in different microtask
51+
// if you don't refer the state in reactive statement.
52+
a += 1
53+
}
54+
55+
// ✗ BAD
56+
$: (async () => {
57+
await doSomething()
58+
// Do not update a state in different micro task.
59+
a += 1
60+
$count += 1
61+
})()
62+
63+
$: tick(() => {
64+
a = a + 1
65+
$count += 1
66+
})
67+
68+
$: (async () => {
69+
console.log(a)
70+
// This rule checks caller function recursively.
71+
await doSomething_ng_1()
72+
})()
73+
74+
const doSomething_ng_1 = async () => {
75+
a += 1
76+
await fetchFromServer()
77+
doSomething_ng_2()
78+
}
79+
80+
const doSomething_ng_2 = () => {
81+
a += 1
82+
}
83+
</script>
84+
```
85+
86+
</ESLintCodeBlock>
87+
88+
## :wrench: Options
89+
90+
Nothing.
91+
92+
## :books: Further Reading
93+
94+
- [Svelte - Docs > COMPONENT FORMAT > 3. $: marks a statement as reactive](https://svelte.dev/docs#component-format-script-3-$-marks-a-statement-as-reactive)
95+
- [Svelte - Docs > COMPONENT FORMAT > 4. Prefix stores with $ to access their values](https://svelte.dev/docs#component-format-script-4-prefix-stores-with-$-to-access-their-values)
96+
97+
## :mag: Implementation
98+
99+
- [Rule source](https://github.com/ota-meshi/eslint-plugin-svelte/blob/main/src/rules/infinite-reactive-loop.ts)
100+
- [Test source](https://github.com/ota-meshi/eslint-plugin-svelte/blob/main/tests/src/rules/infinite-reactive-loop.ts)

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@
174174
"access": "public"
175175
},
176176
"typeCoverage": {
177-
"atLeast": 99.05,
177+
"atLeast": 99.08,
178178
"cache": true,
179179
"detail": true,
180180
"ignoreAsAssertion": true,

0 commit comments

Comments
 (0)