diff --git a/.changeset/wet-kiwis-cover.md b/.changeset/wet-kiwis-cover.md new file mode 100644 index 000000000..a4bff6325 --- /dev/null +++ b/.changeset/wet-kiwis-cover.md @@ -0,0 +1,5 @@ +--- +'eslint-plugin-svelte': patch +--- + +fix(prefer-destructured-store-props): handle runes properly diff --git a/packages/eslint-plugin-svelte/src/rules/prefer-destructured-store-props.ts b/packages/eslint-plugin-svelte/src/rules/prefer-destructured-store-props.ts index 82864254f..5f0bec74f 100644 --- a/packages/eslint-plugin-svelte/src/rules/prefer-destructured-store-props.ts +++ b/packages/eslint-plugin-svelte/src/rules/prefer-destructured-store-props.ts @@ -5,6 +5,8 @@ import { keyword } from 'esutils'; import type { SuggestionReportDescriptor } from '../types.js'; import { createRule } from '../utils/index.js'; import { findAttribute, isExpressionIdentifier, findVariable } from '../utils/ast-utils.js'; +import { getSvelteContext } from '../utils/svelte-context.js'; +import { SVELTE_RUNES } from '../shared/runes.js'; type StoreMemberExpression = TSESTree.MemberExpression & { object: TSESTree.Identifier & { name: string }; @@ -29,6 +31,7 @@ export default createRule('prefer-destructured-store-props', { }, create(context) { let mainScript: AST.SvelteScriptElement | null = null; + const svelteContext = getSvelteContext(context); // Store off instances of probably-destructurable statements const reports: StoreMemberExpression[] = []; @@ -150,6 +153,8 @@ export default createRule('prefer-destructured-store-props', { node: StoreMemberExpression ) { if (inScriptElement) return; // Within a script tag + // Skip Svelte 5 runes (e.g., $derived.by, $state.raw, $effect.pre) + if (svelteContext?.runes === true && SVELTE_RUNES.has(node.object.name)) return; storeMemberAccessStack.unshift({ node, identifiers: [] }); }, Identifier(node: TSESTree.Identifier) { diff --git a/packages/eslint-plugin-svelte/src/shared/runes.ts b/packages/eslint-plugin-svelte/src/shared/runes.ts new file mode 100644 index 000000000..9fd53a679 --- /dev/null +++ b/packages/eslint-plugin-svelte/src/shared/runes.ts @@ -0,0 +1,9 @@ +export const SVELTE_RUNES = new Set([ + '$state', + '$derived', + '$effect', + '$props', + '$bindable', + '$inspect', + '$host' +]); diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-destructured-store-props/invalid/runes-with-store01-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-destructured-store-props/invalid/runes-with-store01-errors.yaml new file mode 100644 index 000000000..838e5e203 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-destructured-store-props/invalid/runes-with-store01-errors.yaml @@ -0,0 +1,18 @@ +- message: Destructure foo from $store for better change tracking & fewer redraws + line: 10 + column: 18 + suggestions: + - desc: 'Using destructuring like $: ({ foo } = $store); will run faster' + messageId: fixUseDestructuring + output: | + + + +

Count: {count}

+

Doubled: {doubled}

+

Store value: {foo}

diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-destructured-store-props/invalid/runes-with-store01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-destructured-store-props/invalid/runes-with-store01-input.svelte new file mode 100644 index 000000000..abd8fbccf --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-destructured-store-props/invalid/runes-with-store01-input.svelte @@ -0,0 +1,10 @@ + + + +

Count: {count}

+

Doubled: {doubled}

+

Store value: {$store.foo}

diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-destructured-store-props/invalid/runes-with-store01-requirements.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-destructured-store-props/invalid/runes-with-store01-requirements.json new file mode 100644 index 000000000..0192b1098 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-destructured-store-props/invalid/runes-with-store01-requirements.json @@ -0,0 +1,3 @@ +{ + "svelte": ">=5.0.0-0" +} diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-destructured-store-props/valid/runes01-input.svelte.js b/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-destructured-store-props/valid/runes01-input.svelte.js new file mode 100644 index 000000000..84972dafa --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-destructured-store-props/valid/runes01-input.svelte.js @@ -0,0 +1,8 @@ +export class Test { + a = $state(0); + b = $derived.by(() => this.a * 2); + + output() { + console.log(this.a, this.b); + } +} \ No newline at end of file diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-destructured-store-props/valid/runes01-requirements.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-destructured-store-props/valid/runes01-requirements.json new file mode 100644 index 000000000..0192b1098 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-destructured-store-props/valid/runes01-requirements.json @@ -0,0 +1,3 @@ +{ + "svelte": ">=5.0.0-0" +}