Skip to content

Commit 160294d

Browse files
committed
implement
1 parent 7a6eba7 commit 160294d

14 files changed

+122
-9
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,7 @@ These rules relate to better ways of doing things to help you avoid problems:
310310
| [svelte/no-useless-mustaches](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-useless-mustaches/) | disallow unnecessary mustache interpolations | :star::wrench: |
311311
| [svelte/prefer-const](https://sveltejs.github.io/eslint-plugin-svelte/rules/prefer-const/) | Require `const` declarations for variables that are never reassigned after declared | :wrench: |
312312
| [svelte/prefer-destructured-store-props](https://sveltejs.github.io/eslint-plugin-svelte/rules/prefer-destructured-store-props/) | destructure values from object stores for better change tracking & fewer redraws | :bulb: |
313+
| [svelte/prefer-writable-derived](https://sveltejs.github.io/eslint-plugin-svelte/rules/prefer-writable-derived/) | Prefer using writable $derived instead of $state and $effect | :star::wrench: |
313314
| [svelte/require-each-key](https://sveltejs.github.io/eslint-plugin-svelte/rules/require-each-key/) | require keyed `{#each}` block | :star: |
314315
| [svelte/require-event-dispatcher-types](https://sveltejs.github.io/eslint-plugin-svelte/rules/require-event-dispatcher-types/) | require type parameters for `createEventDispatcher` | :star: |
315316
| [svelte/require-optimized-style-attribute](https://sveltejs.github.io/eslint-plugin-svelte/rules/require-optimized-style-attribute/) | require style attributes that can be optimized | |

docs/rules.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ These rules relate to better ways of doing things to help you avoid problems:
6767
| [svelte/no-useless-mustaches](./rules/no-useless-mustaches.md) | disallow unnecessary mustache interpolations | :star::wrench: |
6868
| [svelte/prefer-const](./rules/prefer-const.md) | Require `const` declarations for variables that are never reassigned after declared | :wrench: |
6969
| [svelte/prefer-destructured-store-props](./rules/prefer-destructured-store-props.md) | destructure values from object stores for better change tracking & fewer redraws | :bulb: |
70+
| [svelte/prefer-writable-derived](./rules/prefer-writable-derived.md) | Prefer using writable $derived instead of $state and $effect | :star::wrench: |
7071
| [svelte/require-each-key](./rules/require-each-key.md) | require keyed `{#each}` block | :star: |
7172
| [svelte/require-event-dispatcher-types](./rules/require-event-dispatcher-types.md) | require type parameters for `createEventDispatcher` | :star: |
7273
| [svelte/require-optimized-style-attribute](./rules/require-optimized-style-attribute.md) | require style attributes that can be optimized | |

docs/rules/prefer-writable-derived.md

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,48 @@
1-
# (svelte/prefer-writable-derived)
1+
---
2+
pageClass: 'rule-details'
3+
sidebarDepth: 0
4+
title: 'svelte/prefer-writable-derived'
5+
description: 'Prefer using writable $derived instead of $state and $effect'
6+
---
27

3-
> description
8+
# svelte/prefer-writable-derived
9+
10+
> Prefer using writable $derived instead of $state and $effect
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+
- :gear: This rule is included in `"plugin:svelte/recommended"`.
14+
- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
415

516
## :book: Rule Details
617

7-
This rule reports ???.
18+
This rule reports when you use a combination of `$state` and `$effect` to create a derived value that can be written to. It encourages using the more concise and clearer `$derived` syntax instead.
819

920
<!--eslint-skip-->
1021

1122
```svelte
1223
<script>
1324
/* eslint svelte/prefer-writable-derived: "error" */
14-
</script>
25+
const { initialValue } = $props();
1526
16-
<!-- ✓ GOOD -->
27+
// ✓ GOOD
28+
let value1 = $derived(initialValue);
1729
18-
<!-- ✗ BAD -->
30+
// ✗ BAD
31+
let value2 = $state(initialValue);
32+
$effect(() => {
33+
value2 = initialValue;
34+
});
35+
</script>
1936
```
2037

38+
The rule specifically looks for patterns where:
39+
40+
1. You initialize a variable with `$state()`
41+
2. You then use `$effect()` or `$effect.pre()` to assign a new value to that same variable
42+
3. The effect function contains only a single assignment statement
43+
44+
When this pattern is detected, the rule suggests refactoring to use `$derived()` instead, which provides the same functionality in a more concise way.
45+
2146
## :wrench: Options
2247

2348
```json
@@ -26,8 +51,14 @@ This rule reports ???.
2651
}
2752
```
2853

29-
-
54+
- This rule has no options.
3055

3156
## :books: Further Reading
3257

33-
-
58+
- [Svelte Documentation on Reactivity Primitives](https://svelte.dev/docs/svelte-components#script-2-assignments-are-reactive)
59+
- [Svelte RFC for Reactivity Primitives](https://github.com/sveltejs/rfcs/blob/rfc-better-primitives/text/0000-better-primitives.md)
60+
61+
## :mag: Implementation
62+
63+
- [Rule source](https://github.com/sveltejs/eslint-plugin-svelte/blob/main/packages/eslint-plugin-svelte/src/rules/prefer-writable-derived.ts)
64+
- [Test source](https://github.com/sveltejs/eslint-plugin-svelte/blob/main/packages/eslint-plugin-svelte/tests/src/rules/prefer-writable-derived.ts)

packages/eslint-plugin-svelte/src/configs/flat/recommended.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ const config: Linter.Config[] = [
3737
'svelte/no-unused-svelte-ignore': 'error',
3838
'svelte/no-useless-children-snippet': 'error',
3939
'svelte/no-useless-mustaches': 'error',
40+
'svelte/prefer-writable-derived': 'error',
4041
'svelte/require-each-key': 'error',
4142
'svelte/require-event-dispatcher-types': 'error',
4243
'svelte/require-store-reactive-access': 'error',

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,11 @@ export interface RuleOptions {
306306
* @see https://sveltejs.github.io/eslint-plugin-svelte/rules/prefer-style-directive/
307307
*/
308308
'svelte/prefer-style-directive'?: Linter.RuleEntry<[]>
309+
/**
310+
* Prefer using writable $derived instead of $state and $effect
311+
* @see https://sveltejs.github.io/eslint-plugin-svelte/rules/prefer-writable-derived/
312+
*/
313+
'svelte/prefer-writable-derived'?: Linter.RuleEntry<[]>
309314
/**
310315
* require keyed `{#each}` block
311316
* @see https://sveltejs.github.io/eslint-plugin-svelte/rules/require-each-key/

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ import preferClassDirective from '../rules/prefer-class-directive.js';
6060
import preferConst from '../rules/prefer-const.js';
6161
import preferDestructuredStoreProps from '../rules/prefer-destructured-store-props.js';
6262
import preferStyleDirective from '../rules/prefer-style-directive.js';
63-
import preferWritableDerived from 'src/rules/prefer-writable-derived.js';
63+
import preferWritableDerived from '../rules/prefer-writable-derived.js';
6464
import requireEachKey from '../rules/require-each-key.js';
6565
import requireEventDispatcherTypes from '../rules/require-event-dispatcher-types.js';
6666
import requireOptimizedStyleAttribute from '../rules/require-optimized-style-attribute.js';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
- message: Prefer using writable $derived instead of $state and $effect
2+
line: 4
3+
column: 6
4+
suggestions: null
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<script>
2+
const { albumName } = $props();
3+
4+
let newAlbumName = $state(albumName);
5+
$effect(() => {
6+
newAlbumName = albumName;
7+
});
8+
9+
setInterval(() => {
10+
newAlbumName = albumName + albumName;
11+
}, 1000);
12+
</script>
13+
14+
<input bind:value={newAlbumName} />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<script>
2+
const { albumName } = $props();
3+
4+
let newAlbumName = $derived(albumName);
5+
;
6+
7+
setInterval(() => {
8+
newAlbumName = albumName + albumName;
9+
}, 1000);
10+
</script>
11+
12+
<input bind:value={newAlbumName} />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
- message: Prefer using writable $derived instead of $state and $effect
2+
line: 4
3+
column: 6
4+
suggestions: null
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<script>
2+
const { albumName } = $props();
3+
4+
let newAlbumName = $state(albumName);
5+
$effect(() => {
6+
newAlbumName = albumName;
7+
});
8+
</script>
9+
10+
<FooComponent
11+
doSomething={(value) => {
12+
newAlbumName = value;
13+
}}
14+
/>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<script>
2+
const { albumName } = $props();
3+
4+
let newAlbumName = $derived(albumName);
5+
;
6+
</script>
7+
8+
<FooComponent
9+
doSomething={(value) => {
10+
newAlbumName = value;
11+
}}
12+
/>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<script>
2+
const { albumName } = $props();
3+
4+
let newAlbumName = $state(albumName);
5+
$effect(() => {
6+
if (albumName === '') {
7+
newAlbumName = albumName + albumName;
8+
} else {
9+
newAlbumName = albumName;
10+
}
11+
});
12+
</script>
13+
14+
<input bind:value={newAlbumName} />

0 commit comments

Comments
 (0)