Skip to content

Commit 449a2b1

Browse files
committed
feat(prefer-svelte-reactivity): ignoring variables encapsulated in functions
1 parent 856db6f commit 449a2b1

File tree

4 files changed

+55
-3
lines changed

4 files changed

+55
-3
lines changed

.changeset/long-ghosts-prove.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'eslint-plugin-svelte': minor
3+
---
4+
5+
feat(prefer-svelte-reactivity): ignoring variables encapsulated in functions

docs/rules/prefer-svelte-reactivity.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,18 @@ export default e;
105105

106106
## :wrench: Options
107107

108-
Nothing.
108+
```json
109+
{
110+
"svelte/prefer-svelte-reactivity": [
111+
"error",
112+
{
113+
"ignoreEncapsulatedLocalVariables": true
114+
}
115+
]
116+
}
117+
```
118+
119+
- `ignoreEncapsulatedLocalVariables` ... Whether to ignore variables that are defined inside a function and aren't returned, thus being encapsulated in the function. Default `true`.
109120

110121
## :books: Further Reading
111122

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ export interface RuleOptions {
320320
* disallow using mutable instances of built-in classes where a reactive alternative is provided by svelte/reactivity
321321
* @see https://sveltejs.github.io/eslint-plugin-svelte/rules/prefer-svelte-reactivity/
322322
*/
323-
'svelte/prefer-svelte-reactivity'?: Linter.RuleEntry<[]>
323+
'svelte/prefer-svelte-reactivity'?: Linter.RuleEntry<SveltePreferSvelteReactivity>
324324
/**
325325
* Prefer using writable $derived instead of $state and $effect
326326
* @see https://sveltejs.github.io/eslint-plugin-svelte/rules/prefer-writable-derived/
@@ -580,6 +580,10 @@ type SveltePreferConst = []|[{
580580
excludedRunes?: string[]
581581
[k: string]: unknown | undefined
582582
}]
583+
// ----- svelte/prefer-svelte-reactivity -----
584+
type SveltePreferSvelteReactivity = []|[{
585+
ignoreEncapsulatedLocalVariables?: boolean
586+
}]
583587
// ----- svelte/require-event-prefix -----
584588
type SvelteRequireEventPrefix = []|[{
585589
checkAsyncFunctions?: boolean

packages/eslint-plugin-svelte/src/rules/prefer-svelte-reactivity.ts

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,18 @@ export default createRule('prefer-svelte-reactivity', {
1212
category: 'Possible Errors',
1313
recommended: true
1414
},
15-
schema: [],
15+
schema: [
16+
{
17+
type: 'object',
18+
properties: {
19+
ignoreEncapsulatedLocalVariables: {
20+
type: 'boolean',
21+
default: true
22+
}
23+
},
24+
additionalProperties: false
25+
}
26+
],
1627
messages: {
1728
mutableDateUsed:
1829
'Found a mutable instance of the built-in Date class. Use SvelteDate instead.',
@@ -31,6 +42,8 @@ export default createRule('prefer-svelte-reactivity', {
3142
]
3243
},
3344
create(context) {
45+
const ignoreEncapsulatedLocalVariables =
46+
context.options[0]?.ignoreEncapsulatedLocalVariables ?? true;
3447
const returnedVariables: Map<
3548
TSESTree.ArrowFunctionExpression | TSESTree.FunctionDeclaration,
3649
TSESTree.VariableDeclarator[]
@@ -139,6 +152,9 @@ export default createRule('prefer-svelte-reactivity', {
139152
node
140153
});
141154
}
155+
if (ignoreEncapsulatedLocalVariables && isLocalVarEncapsulated(returnedVariables, node)) {
156+
continue;
157+
}
142158
if (path[0] === 'Date' && isDateMutable(referenceTracker, node as TSESTree.Expression)) {
143159
context.report({
144160
messageId: 'mutableDateUsed',
@@ -205,6 +221,22 @@ function findEnclosingReturn(node: TSESTree.Node): TSESTree.ReturnStatement | nu
205221
return findAncestorOfTypes(node, ['ReturnStatement']);
206222
}
207223

224+
function isLocalVarEncapsulated(
225+
returnedVariables: Map<
226+
TSESTree.ArrowFunctionExpression | TSESTree.FunctionDeclaration,
227+
TSESTree.VariableDeclarator[]
228+
>,
229+
node: TSESTree.Node
230+
): boolean {
231+
const enclosingFunction = findEnclosingFunction(node);
232+
if (enclosingFunction === null) {
233+
return false;
234+
}
235+
return (
236+
returnedVariables.get(enclosingFunction)?.some((variable) => isIn(node, variable)) !== true
237+
);
238+
}
239+
208240
function isDateMutable(referenceTracker: ReferenceTracker, ctorNode: TSESTree.Expression): boolean {
209241
return !referenceTracker
210242
.iteratePropertyReferences(ctorNode, {

0 commit comments

Comments
 (0)