Skip to content

Commit 86c7cba

Browse files
committed
fix(prefer-destructured-store-props): handle runes properly
1 parent 89d030d commit 86c7cba

File tree

7 files changed

+65
-0
lines changed

7 files changed

+65
-0
lines changed

.changeset/wet-kiwis-cover.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'eslint-plugin-svelte': patch
3+
---
4+
5+
fix(prefer-destructured-store-props): handle runes properly

packages/eslint-plugin-svelte/src/rules/prefer-destructured-store-props.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,26 @@ import { keyword } from 'esutils';
55
import type { SuggestionReportDescriptor } from '../types.js';
66
import { createRule } from '../utils/index.js';
77
import { findAttribute, isExpressionIdentifier, findVariable } from '../utils/ast-utils.js';
8+
import { getSvelteContext } from '../utils/svelte-context.js';
89

910
type StoreMemberExpression = TSESTree.MemberExpression & {
1011
object: TSESTree.Identifier & { name: string };
1112
};
1213

14+
/**
15+
* Svelte 5 runes that start with `$` but are not stores.
16+
* These should be excluded from the prefer-destructured-store-props rule.
17+
*/
18+
const SVELTE_RUNES = new Set([
19+
'$state',
20+
'$derived',
21+
'$effect',
22+
'$props',
23+
'$bindable',
24+
'$inspect',
25+
'$host'
26+
]);
27+
1328
export default createRule('prefer-destructured-store-props', {
1429
meta: {
1530
docs: {
@@ -29,6 +44,7 @@ export default createRule('prefer-destructured-store-props', {
2944
},
3045
create(context) {
3146
let mainScript: AST.SvelteScriptElement | null = null;
47+
const svelteContext = getSvelteContext(context);
3248

3349
// Store off instances of probably-destructurable statements
3450
const reports: StoreMemberExpression[] = [];
@@ -150,6 +166,8 @@ export default createRule('prefer-destructured-store-props', {
150166
node: StoreMemberExpression
151167
) {
152168
if (inScriptElement) return; // Within a script tag
169+
// Skip Svelte 5 runes (e.g., $derived.by, $state.raw, $effect.pre)
170+
if (svelteContext?.runes === true && SVELTE_RUNES.has(node.object.name)) return;
153171
storeMemberAccessStack.unshift({ node, identifiers: [] });
154172
},
155173
Identifier(node: TSESTree.Identifier) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
- message: Destructure foo from $store for better change tracking & fewer redraws
2+
line: 10
3+
column: 18
4+
suggestions:
5+
- desc: 'Using destructuring like $: ({ foo } = $store); will run faster'
6+
messageId: fixUseDestructuring
7+
output: |
8+
<script>
9+
import store from './store.js';
10+
let count = $state(0);
11+
let doubled = $derived.by(() => count * 2);
12+
$: ({ foo } = $store);
13+
</script>
14+
15+
<!-- $state and $derived.by should be ignored (runes), but $store.foo should be reported -->
16+
<p>Count: {count}</p>
17+
<p>Doubled: {doubled}</p>
18+
<p>Store value: {foo}</p>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<script>
2+
import store from './store.js';
3+
let count = $state(0);
4+
let doubled = $derived.by(() => count * 2);
5+
</script>
6+
7+
<!-- $state and $derived.by should be ignored (runes), but $store.foo should be reported -->
8+
<p>Count: {count}</p>
9+
<p>Doubled: {doubled}</p>
10+
<p>Store value: {$store.foo}</p>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"svelte": ">=5.0.0-0"
3+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export class Test {
2+
a = $state(0);
3+
b = $derived.by(() => this.a * 2);
4+
5+
output() {
6+
console.log(this.a, this.b);
7+
}
8+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"svelte": ">=5.0.0-0"
3+
}

0 commit comments

Comments
 (0)