Skip to content

Commit fe63e88

Browse files
committed
feat: signal-prefer-let rule
1 parent f8f377f commit fe63e88

File tree

10 files changed

+158
-0
lines changed

10 files changed

+158
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,7 @@ These rules relate to better ways of doing things to help you avoid problems:
430430
| [svelte/require-event-dispatcher-types](https://sveltejs.github.io/eslint-plugin-svelte/rules/require-event-dispatcher-types/) | require type parameters for `createEventDispatcher` | |
431431
| [svelte/require-optimized-style-attribute](https://sveltejs.github.io/eslint-plugin-svelte/rules/require-optimized-style-attribute/) | require style attributes that can be optimized | |
432432
| [svelte/require-stores-init](https://sveltejs.github.io/eslint-plugin-svelte/rules/require-stores-init/) | require initial value in store | |
433+
| [svelte/signal-prefer-let](https://sveltejs.github.io/eslint-plugin-svelte/rules/signal-prefer-let/) | use let instead of const for signals values | :bulb: |
433434
| [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 | |
434435

435436
## Stylistic Issues

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/require-event-dispatcher-types](./rules/require-event-dispatcher-types.md) | require type parameters for `createEventDispatcher` | |
6868
| [svelte/require-optimized-style-attribute](./rules/require-optimized-style-attribute.md) | require style attributes that can be optimized | |
6969
| [svelte/require-stores-init](./rules/require-stores-init.md) | require initial value in store | |
70+
| [svelte/signal-prefer-let](./rules/signal-prefer-let.md) | use let instead of const for signals values | :bulb: |
7071
| [svelte/valid-each-key](./rules/valid-each-key.md) | enforce keys to use variables defined in the `{#each}` block | |
7172

7273
## Stylistic Issues

docs/rules/signal-prefer-let.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
---
2+
pageClass: 'rule-details'
3+
sidebarDepth: 0
4+
title: 'svelte/signal-prefer-let'
5+
description: 'use let instead of const for signals values'
6+
---
7+
8+
# svelte/signal-prefer-let
9+
10+
> use let instead of const for signals values
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+
- :bulb: Some problems reported by this rule are manually fixable by editor [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions).
14+
15+
## :book: Rule Details
16+
17+
This rule reports whenever a signal is assigned to a const.
18+
In JavaScript `const` are defined as immutable references which cannot be reassigned.
19+
Signals are by definition changing and are reassigned by Svelte's reactivity system.
20+
21+
<ESLintCodeBlock>
22+
23+
<!--eslint-skip-->
24+
25+
```svelte
26+
<script>
27+
/* eslint svelte/signal-prefer-let: "error" */
28+
29+
/* ✓ GOOD */
30+
let { value } = $props();
31+
32+
let doubled = $derived(value * 2);
33+
34+
/* ✗ BAD */
35+
const { value } = $props();
36+
37+
const doubled = $derived(value * 2);
38+
</script>
39+
```
40+
41+
</ESLintCodeBlock>
42+
43+
## :wrench: Options
44+
45+
Nothing
46+
47+
## :mag: Implementation
48+
49+
- [Rule source](https://github.com/sveltejs/eslint-plugin-svelte/blob/main/src/rules/signal-prefer-let.ts)
50+
- [Test source](https://github.com/sveltejs/eslint-plugin-svelte/blob/main/tests/src/rules/signal-prefer-let.ts)

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,11 @@ export interface RuleOptions {
299299
* @see https://sveltejs.github.io/eslint-plugin-svelte/rules/shorthand-directive/
300300
*/
301301
'svelte/shorthand-directive'?: Linter.RuleEntry<SvelteShorthandDirective>
302+
/**
303+
* use let instead of const for signals values
304+
* @see https://sveltejs.github.io/eslint-plugin-svelte/rules/signal-prefer-let/
305+
*/
306+
'svelte/signal-prefer-let'?: Linter.RuleEntry<[]>
302307
/**
303308
* enforce order of attributes
304309
* @see https://sveltejs.github.io/eslint-plugin-svelte/rules/sort-attributes/
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import type { TSESTree } from '@typescript-eslint/types';
2+
import { createRule } from '../utils';
3+
4+
export default createRule('signal-prefer-let', {
5+
meta: {
6+
docs: {
7+
description: 'use let instead of const for signals values',
8+
category: 'Best Practices',
9+
recommended: false
10+
},
11+
schema: [],
12+
messages: {
13+
useLet: "const is used for a signal value. Use 'let' instead.",
14+
replaceConst: "Replace 'const' with 'let'"
15+
},
16+
type: 'suggestion',
17+
hasSuggestions: true
18+
},
19+
create(context) {
20+
function preferLet(node: TSESTree.VariableDeclaration) {
21+
if (node.kind !== 'const') {
22+
return;
23+
}
24+
context.report({
25+
node,
26+
messageId: 'useLet',
27+
suggest: [
28+
{
29+
messageId: 'replaceConst',
30+
fix: (fixer) => fixer.replaceTextRange([node.range[0], node.range[0] + 5], 'let')
31+
}
32+
]
33+
});
34+
}
35+
36+
return {
37+
'VariableDeclaration > VariableDeclarator > CallExpression > Identifier'(
38+
node: TSESTree.Identifier
39+
) {
40+
if (['$props', '$derived', '$state'].includes(node.name)) {
41+
preferLet(node.parent.parent?.parent as TSESTree.VariableDeclaration);
42+
}
43+
},
44+
'VariableDeclaration > VariableDeclarator > CallExpression > MemberExpression > Identifier'(
45+
node: TSESTree.Identifier
46+
) {
47+
if (
48+
node.name === 'by' &&
49+
((node.parent as TSESTree.MemberExpression).object as TSESTree.Identifier).name ===
50+
'$derived'
51+
) {
52+
preferLet(node.parent.parent?.parent?.parent as TSESTree.VariableDeclaration);
53+
}
54+
}
55+
};
56+
}
57+
});

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ import requireStoreReactiveAccess from '../rules/require-store-reactive-access';
5959
import requireStoresInit from '../rules/require-stores-init';
6060
import shorthandAttribute from '../rules/shorthand-attribute';
6161
import shorthandDirective from '../rules/shorthand-directive';
62+
import signalPreferLet from '../rules/signal-prefer-let';
6263
import sortAttributes from '../rules/sort-attributes';
6364
import spacedHtmlComment from '../rules/spaced-html-comment';
6465
import system from '../rules/system';
@@ -124,6 +125,7 @@ export const rules = [
124125
requireStoresInit,
125126
shorthandAttribute,
126127
shorthandDirective,
128+
signalPreferLet,
127129
sortAttributes,
128130
spacedHtmlComment,
129131
system,
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
- message: "const is used for a signal value. Use 'let' instead."
2+
line: 2
3+
column: 2
4+
suggestions:
5+
- desc: "Replace 'const' with 'let'"
6+
output: |
7+
<script>
8+
let { value, fn } = $props();
9+
10+
const x = $derived.by(fn);
11+
</script>
12+
- message: "const is used for a signal value. Use 'let' instead."
13+
line: 4
14+
column: 2
15+
suggestions:
16+
- desc: "Replace 'const' with 'let'"
17+
output: |
18+
<script>
19+
const { value, fn } = $props();
20+
21+
let x = $derived.by(fn);
22+
</script>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<script>
2+
const { value, fn } = $props();
3+
4+
const x = $derived.by(fn);
5+
</script>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<script>
2+
let { value } = $props();
3+
</script>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { RuleTester } from '../../utils/eslint-compat';
2+
import rule from '../../../src/rules/signal-prefer-let';
3+
import { loadTestCases } from '../../utils/utils';
4+
5+
const tester = new RuleTester({
6+
languageOptions: {
7+
ecmaVersion: 2020,
8+
sourceType: 'module'
9+
}
10+
});
11+
12+
tester.run('signal-prefer-let', rule as any, loadTestCases('signal-prefer-let'));

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy