From f5775b7fce9736c6857f199eb01a3718d83a3586 Mon Sep 17 00:00:00 2001 From: Bob Fanger Date: Sat, 22 Jun 2024 19:45:15 +0200 Subject: [PATCH 1/2] feat: signal-prefer-let rule --- .changeset/proud-donuts-tickle.md | 5 ++ README.md | 1 + docs/rules.md | 1 + docs/rules/signal-prefer-let.md | 50 ++++++++++++++++++ .../eslint-plugin-svelte/src/rule-types.ts | 5 ++ .../src/rules/signal-prefer-let.ts | 51 +++++++++++++++++++ .../eslint-plugin-svelte/src/utils/rules.ts | 2 + .../invalid/test01-errors.yaml | 12 +++++ .../invalid/test01-input.svelte | 6 +++ .../invalid/test01-output.svelte | 6 +++ .../valid/test01-input.svelte | 3 ++ .../tests/src/rules/signal-prefer-let.ts | 12 +++++ 12 files changed, 154 insertions(+) create mode 100644 .changeset/proud-donuts-tickle.md create mode 100644 docs/rules/signal-prefer-let.md create mode 100644 packages/eslint-plugin-svelte/src/rules/signal-prefer-let.ts create mode 100644 packages/eslint-plugin-svelte/tests/fixtures/rules/signal-prefer-let/invalid/test01-errors.yaml create mode 100644 packages/eslint-plugin-svelte/tests/fixtures/rules/signal-prefer-let/invalid/test01-input.svelte create mode 100644 packages/eslint-plugin-svelte/tests/fixtures/rules/signal-prefer-let/invalid/test01-output.svelte create mode 100644 packages/eslint-plugin-svelte/tests/fixtures/rules/signal-prefer-let/valid/test01-input.svelte create mode 100644 packages/eslint-plugin-svelte/tests/src/rules/signal-prefer-let.ts diff --git a/.changeset/proud-donuts-tickle.md b/.changeset/proud-donuts-tickle.md new file mode 100644 index 000000000..a528563c8 --- /dev/null +++ b/.changeset/proud-donuts-tickle.md @@ -0,0 +1,5 @@ +--- +'eslint-plugin-svelte': minor +--- + +New svelte/signal-prefer-let rule diff --git a/README.md b/README.md index 9a6c25600..858bc4e52 100644 --- a/README.md +++ b/README.md @@ -430,6 +430,7 @@ These rules relate to better ways of doing things to help you avoid problems: | [svelte/require-event-dispatcher-types](https://sveltejs.github.io/eslint-plugin-svelte/rules/require-event-dispatcher-types/) | require type parameters for `createEventDispatcher` | | | [svelte/require-optimized-style-attribute](https://sveltejs.github.io/eslint-plugin-svelte/rules/require-optimized-style-attribute/) | require style attributes that can be optimized | | | [svelte/require-stores-init](https://sveltejs.github.io/eslint-plugin-svelte/rules/require-stores-init/) | require initial value in store | | +| [svelte/signal-prefer-let](https://sveltejs.github.io/eslint-plugin-svelte/rules/signal-prefer-let/) | use let instead of const for signals values | :wrench: | | [svelte/valid-each-key](https://sveltejs.github.io/eslint-plugin-svelte/rules/valid-each-key/) | enforce keys to use variables defined in the `{#each}` block | | ## Stylistic Issues diff --git a/docs/rules.md b/docs/rules.md index 7f115da5c..d89287762 100644 --- a/docs/rules.md +++ b/docs/rules.md @@ -67,6 +67,7 @@ These rules relate to better ways of doing things to help you avoid problems: | [svelte/require-event-dispatcher-types](./rules/require-event-dispatcher-types.md) | require type parameters for `createEventDispatcher` | | | [svelte/require-optimized-style-attribute](./rules/require-optimized-style-attribute.md) | require style attributes that can be optimized | | | [svelte/require-stores-init](./rules/require-stores-init.md) | require initial value in store | | +| [svelte/signal-prefer-let](./rules/signal-prefer-let.md) | use let instead of const for signals values | :wrench: | | [svelte/valid-each-key](./rules/valid-each-key.md) | enforce keys to use variables defined in the `{#each}` block | | ## Stylistic Issues diff --git a/docs/rules/signal-prefer-let.md b/docs/rules/signal-prefer-let.md new file mode 100644 index 000000000..44f59a527 --- /dev/null +++ b/docs/rules/signal-prefer-let.md @@ -0,0 +1,50 @@ +--- +pageClass: 'rule-details' +sidebarDepth: 0 +title: 'svelte/signal-prefer-let' +description: 'use let instead of const for signals values' +--- + +# svelte/signal-prefer-let + +> use let instead of const for signals values + +- :exclamation: **_This rule has not been released yet._** +- :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. + +## :book: Rule Details + +This rule reports whenever a signal is assigned to a const. +In JavaScript `const` are defined as immutable references which cannot be reassigned. +Signals are by definition changing and are reassigned by Svelte's reactivity system. + + + + + +```svelte + +``` + + + +## :wrench: Options + +Nothing + +## :mag: Implementation + +- [Rule source](https://github.com/sveltejs/eslint-plugin-svelte/blob/main/src/rules/signal-prefer-let.ts) +- [Test source](https://github.com/sveltejs/eslint-plugin-svelte/blob/main/tests/src/rules/signal-prefer-let.ts) diff --git a/packages/eslint-plugin-svelte/src/rule-types.ts b/packages/eslint-plugin-svelte/src/rule-types.ts index 77d3c4ca4..3c1882aca 100644 --- a/packages/eslint-plugin-svelte/src/rule-types.ts +++ b/packages/eslint-plugin-svelte/src/rule-types.ts @@ -299,6 +299,11 @@ export interface RuleOptions { * @see https://sveltejs.github.io/eslint-plugin-svelte/rules/shorthand-directive/ */ 'svelte/shorthand-directive'?: Linter.RuleEntry + /** + * use let instead of const for signals values + * @see https://sveltejs.github.io/eslint-plugin-svelte/rules/signal-prefer-let/ + */ + 'svelte/signal-prefer-let'?: Linter.RuleEntry<[]> /** * enforce order of attributes * @see https://sveltejs.github.io/eslint-plugin-svelte/rules/sort-attributes/ diff --git a/packages/eslint-plugin-svelte/src/rules/signal-prefer-let.ts b/packages/eslint-plugin-svelte/src/rules/signal-prefer-let.ts new file mode 100644 index 000000000..cf5043912 --- /dev/null +++ b/packages/eslint-plugin-svelte/src/rules/signal-prefer-let.ts @@ -0,0 +1,51 @@ +import type { TSESTree } from '@typescript-eslint/types'; +import { createRule } from '../utils'; + +export default createRule('signal-prefer-let', { + meta: { + docs: { + description: 'use let instead of const for signals values', + category: 'Best Practices', + recommended: false + }, + schema: [], + messages: { + useLet: "const is used for a signal value. Use 'let' instead." + }, + type: 'suggestion', + fixable: 'code' + }, + create(context) { + function preferLet(node: TSESTree.VariableDeclaration) { + if (node.kind !== 'const') { + return; + } + context.report({ + node, + messageId: 'useLet', + fix: (fixer) => fixer.replaceTextRange([node.range[0], node.range[0] + 5], 'let') + }); + } + + return { + 'VariableDeclaration > VariableDeclarator > CallExpression > Identifier'( + node: TSESTree.Identifier + ) { + if (['$props', '$derived', '$state'].includes(node.name)) { + preferLet(node.parent.parent?.parent as TSESTree.VariableDeclaration); + } + }, + 'VariableDeclaration > VariableDeclarator > CallExpression > MemberExpression > Identifier'( + node: TSESTree.Identifier + ) { + if ( + node.name === 'by' && + ((node.parent as TSESTree.MemberExpression).object as TSESTree.Identifier).name === + '$derived' + ) { + preferLet(node.parent.parent?.parent?.parent as TSESTree.VariableDeclaration); + } + } + }; + } +}); diff --git a/packages/eslint-plugin-svelte/src/utils/rules.ts b/packages/eslint-plugin-svelte/src/utils/rules.ts index af0dd20e6..653214bb1 100644 --- a/packages/eslint-plugin-svelte/src/utils/rules.ts +++ b/packages/eslint-plugin-svelte/src/utils/rules.ts @@ -59,6 +59,7 @@ import requireStoreReactiveAccess from '../rules/require-store-reactive-access'; import requireStoresInit from '../rules/require-stores-init'; import shorthandAttribute from '../rules/shorthand-attribute'; import shorthandDirective from '../rules/shorthand-directive'; +import signalPreferLet from '../rules/signal-prefer-let'; import sortAttributes from '../rules/sort-attributes'; import spacedHtmlComment from '../rules/spaced-html-comment'; import system from '../rules/system'; @@ -124,6 +125,7 @@ export const rules = [ requireStoresInit, shorthandAttribute, shorthandDirective, + signalPreferLet, sortAttributes, spacedHtmlComment, system, diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/signal-prefer-let/invalid/test01-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/signal-prefer-let/invalid/test01-errors.yaml new file mode 100644 index 000000000..b432886c3 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/signal-prefer-let/invalid/test01-errors.yaml @@ -0,0 +1,12 @@ +- message: "const is used for a signal value. Use 'let' instead." + line: 2 + column: 2 +- message: "const is used for a signal value. Use 'let' instead." + line: 3 + column: 2 +- message: "const is used for a signal value. Use 'let' instead." + line: 4 + column: 2 +- message: "const is used for a signal value. Use 'let' instead." + line: 5 + column: 2 diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/signal-prefer-let/invalid/test01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/signal-prefer-let/invalid/test01-input.svelte new file mode 100644 index 000000000..c62bac8b0 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/signal-prefer-let/invalid/test01-input.svelte @@ -0,0 +1,6 @@ + diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/signal-prefer-let/invalid/test01-output.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/signal-prefer-let/invalid/test01-output.svelte new file mode 100644 index 000000000..6b344da40 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/signal-prefer-let/invalid/test01-output.svelte @@ -0,0 +1,6 @@ + diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/signal-prefer-let/valid/test01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/signal-prefer-let/valid/test01-input.svelte new file mode 100644 index 000000000..720186465 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/signal-prefer-let/valid/test01-input.svelte @@ -0,0 +1,3 @@ + diff --git a/packages/eslint-plugin-svelte/tests/src/rules/signal-prefer-let.ts b/packages/eslint-plugin-svelte/tests/src/rules/signal-prefer-let.ts new file mode 100644 index 000000000..589f43e6e --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/src/rules/signal-prefer-let.ts @@ -0,0 +1,12 @@ +import { RuleTester } from '../../utils/eslint-compat'; +import rule from '../../../src/rules/signal-prefer-let'; +import { loadTestCases } from '../../utils/utils'; + +const tester = new RuleTester({ + languageOptions: { + ecmaVersion: 2020, + sourceType: 'module' + } +}); + +tester.run('signal-prefer-let', rule as any, loadTestCases('signal-prefer-let')); From 642e4dd8a6cc23bcbb05d8af0205d5789d968b9a Mon Sep 17 00:00:00 2001 From: Bob Fanger Date: Sun, 30 Jun 2024 10:58:31 +0200 Subject: [PATCH 2/2] refactor: Instead of "signal", use wording used in the Svelte docs --- .changeset/proud-donuts-tickle.md | 2 +- README.md | 2 +- docs/rules.md | 2 +- ...ignal-prefer-let.md => rune-prefer-let.md} | 18 ++++++------- .../eslint-plugin-svelte/src/rule-types.ts | 10 +++---- ...ignal-prefer-let.ts => rune-prefer-let.ts} | 26 ++++++++++++++----- .../eslint-plugin-svelte/src/utils/rules.ts | 4 +-- .../invalid/test01-errors.yaml | 20 ++++++++++++++ .../invalid/test01-input.svelte | 3 +++ .../invalid/test01-output.svelte | 3 +++ .../rune-prefer-let/valid/test01-input.svelte | 9 +++++++ .../invalid/test01-errors.yaml | 12 --------- .../valid/test01-input.svelte | 3 --- ...ignal-prefer-let.ts => rune-prefer-let.ts} | 4 +-- 14 files changed, 76 insertions(+), 42 deletions(-) rename docs/rules/{signal-prefer-let.md => rune-prefer-let.md} (65%) rename packages/eslint-plugin-svelte/src/rules/{signal-prefer-let.ts => rune-prefer-let.ts} (58%) create mode 100644 packages/eslint-plugin-svelte/tests/fixtures/rules/rune-prefer-let/invalid/test01-errors.yaml rename packages/eslint-plugin-svelte/tests/fixtures/rules/{signal-prefer-let => rune-prefer-let}/invalid/test01-input.svelte (82%) rename packages/eslint-plugin-svelte/tests/fixtures/rules/{signal-prefer-let => rune-prefer-let}/invalid/test01-output.svelte (82%) create mode 100644 packages/eslint-plugin-svelte/tests/fixtures/rules/rune-prefer-let/valid/test01-input.svelte delete mode 100644 packages/eslint-plugin-svelte/tests/fixtures/rules/signal-prefer-let/invalid/test01-errors.yaml delete mode 100644 packages/eslint-plugin-svelte/tests/fixtures/rules/signal-prefer-let/valid/test01-input.svelte rename packages/eslint-plugin-svelte/tests/src/rules/{signal-prefer-let.ts => rune-prefer-let.ts} (60%) diff --git a/.changeset/proud-donuts-tickle.md b/.changeset/proud-donuts-tickle.md index a528563c8..68a2fbaf0 100644 --- a/.changeset/proud-donuts-tickle.md +++ b/.changeset/proud-donuts-tickle.md @@ -2,4 +2,4 @@ 'eslint-plugin-svelte': minor --- -New svelte/signal-prefer-let rule +New svelte/rune-prefer-let rule diff --git a/README.md b/README.md index 858bc4e52..b5bf563a5 100644 --- a/README.md +++ b/README.md @@ -430,7 +430,7 @@ These rules relate to better ways of doing things to help you avoid problems: | [svelte/require-event-dispatcher-types](https://sveltejs.github.io/eslint-plugin-svelte/rules/require-event-dispatcher-types/) | require type parameters for `createEventDispatcher` | | | [svelte/require-optimized-style-attribute](https://sveltejs.github.io/eslint-plugin-svelte/rules/require-optimized-style-attribute/) | require style attributes that can be optimized | | | [svelte/require-stores-init](https://sveltejs.github.io/eslint-plugin-svelte/rules/require-stores-init/) | require initial value in store | | -| [svelte/signal-prefer-let](https://sveltejs.github.io/eslint-plugin-svelte/rules/signal-prefer-let/) | use let instead of const for signals values | :wrench: | +| [svelte/rune-prefer-let](https://sveltejs.github.io/eslint-plugin-svelte/rules/rune-prefer-let/) | use let instead of const for reactive variables created by runes | :wrench: | | [svelte/valid-each-key](https://sveltejs.github.io/eslint-plugin-svelte/rules/valid-each-key/) | enforce keys to use variables defined in the `{#each}` block | | ## Stylistic Issues diff --git a/docs/rules.md b/docs/rules.md index d89287762..2412386c8 100644 --- a/docs/rules.md +++ b/docs/rules.md @@ -67,7 +67,7 @@ These rules relate to better ways of doing things to help you avoid problems: | [svelte/require-event-dispatcher-types](./rules/require-event-dispatcher-types.md) | require type parameters for `createEventDispatcher` | | | [svelte/require-optimized-style-attribute](./rules/require-optimized-style-attribute.md) | require style attributes that can be optimized | | | [svelte/require-stores-init](./rules/require-stores-init.md) | require initial value in store | | -| [svelte/signal-prefer-let](./rules/signal-prefer-let.md) | use let instead of const for signals values | :wrench: | +| [svelte/rune-prefer-let](./rules/rune-prefer-let.md) | use let instead of const for reactive variables created by runes | :wrench: | | [svelte/valid-each-key](./rules/valid-each-key.md) | enforce keys to use variables defined in the `{#each}` block | | ## Stylistic Issues diff --git a/docs/rules/signal-prefer-let.md b/docs/rules/rune-prefer-let.md similarity index 65% rename from docs/rules/signal-prefer-let.md rename to docs/rules/rune-prefer-let.md index 44f59a527..502d74fc1 100644 --- a/docs/rules/signal-prefer-let.md +++ b/docs/rules/rune-prefer-let.md @@ -1,22 +1,22 @@ --- pageClass: 'rule-details' sidebarDepth: 0 -title: 'svelte/signal-prefer-let' -description: 'use let instead of const for signals values' +title: 'svelte/rune-prefer-let' +description: 'use let instead of const for reactive variables created by runes' --- -# svelte/signal-prefer-let +# svelte/rune-prefer-let -> use let instead of const for signals values +> use let instead of const for reactive variables created by runes - :exclamation: **_This rule has not been released yet._** - :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. ## :book: Rule Details -This rule reports whenever a signal is assigned to a const. +This rule reports whenever a rune that creates a reactive value is assigned to a const. In JavaScript `const` are defined as immutable references which cannot be reassigned. -Signals are by definition changing and are reassigned by Svelte's reactivity system. +Reactive variables can be reassigned by Svelte's reactivity system. @@ -24,7 +24,7 @@ Signals are by definition changing and are reassigned by Svelte's reactivity sys ```svelte diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/signal-prefer-let/invalid/test01-output.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/rune-prefer-let/invalid/test01-output.svelte similarity index 82% rename from packages/eslint-plugin-svelte/tests/fixtures/rules/signal-prefer-let/invalid/test01-output.svelte rename to packages/eslint-plugin-svelte/tests/fixtures/rules/rune-prefer-let/invalid/test01-output.svelte index 6b344da40..ad50f1d6e 100644 --- a/packages/eslint-plugin-svelte/tests/fixtures/rules/signal-prefer-let/invalid/test01-output.svelte +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/rune-prefer-let/invalid/test01-output.svelte @@ -1,6 +1,9 @@ diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/rune-prefer-let/valid/test01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/rune-prefer-let/valid/test01-input.svelte new file mode 100644 index 000000000..ad50f1d6e --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/rune-prefer-let/valid/test01-input.svelte @@ -0,0 +1,9 @@ + diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/signal-prefer-let/invalid/test01-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/signal-prefer-let/invalid/test01-errors.yaml deleted file mode 100644 index b432886c3..000000000 --- a/packages/eslint-plugin-svelte/tests/fixtures/rules/signal-prefer-let/invalid/test01-errors.yaml +++ /dev/null @@ -1,12 +0,0 @@ -- message: "const is used for a signal value. Use 'let' instead." - line: 2 - column: 2 -- message: "const is used for a signal value. Use 'let' instead." - line: 3 - column: 2 -- message: "const is used for a signal value. Use 'let' instead." - line: 4 - column: 2 -- message: "const is used for a signal value. Use 'let' instead." - line: 5 - column: 2 diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/signal-prefer-let/valid/test01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/signal-prefer-let/valid/test01-input.svelte deleted file mode 100644 index 720186465..000000000 --- a/packages/eslint-plugin-svelte/tests/fixtures/rules/signal-prefer-let/valid/test01-input.svelte +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/packages/eslint-plugin-svelte/tests/src/rules/signal-prefer-let.ts b/packages/eslint-plugin-svelte/tests/src/rules/rune-prefer-let.ts similarity index 60% rename from packages/eslint-plugin-svelte/tests/src/rules/signal-prefer-let.ts rename to packages/eslint-plugin-svelte/tests/src/rules/rune-prefer-let.ts index 589f43e6e..a7e923a3d 100644 --- a/packages/eslint-plugin-svelte/tests/src/rules/signal-prefer-let.ts +++ b/packages/eslint-plugin-svelte/tests/src/rules/rune-prefer-let.ts @@ -1,5 +1,5 @@ import { RuleTester } from '../../utils/eslint-compat'; -import rule from '../../../src/rules/signal-prefer-let'; +import rule from '../../../src/rules/rune-prefer-let'; import { loadTestCases } from '../../utils/utils'; const tester = new RuleTester({ @@ -9,4 +9,4 @@ const tester = new RuleTester({ } }); -tester.run('signal-prefer-let', rule as any, loadTestCases('signal-prefer-let')); +tester.run('rune-prefer-let', rule as any, loadTestCases('rune-prefer-let'));