Skip to content

Commit d61457c

Browse files
committed
implement
1 parent 3021f66 commit d61457c

File tree

9 files changed

+101
-6
lines changed

9 files changed

+101
-6
lines changed

.changeset/petite-shirts-smash.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: add `ignoreLocalVariables` to `prefer-svelte-reactivity` rule to avoid false positives

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+
"ignoreLocalVariables": true
114+
}
115+
]
116+
}
117+
```
118+
119+
- `ignoreLocalVariables` ... Set to `true` to ignore variables declared anywhere other than the top level, such as inside functions. The default is `true`. In almost all cases, we do not need to set this to `false`.
109120

110121
## :books: Further Reading
111122

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// IMPORTANT!
22
// This file has been automatically generated,
33
// in order to update its content execute "pnpm run update"
4-
export const name = 'eslint-plugin-svelte' as const;
5-
export const version = '3.11.0' as const;
4+
export const name = 'eslint-plugin-svelte';
5+
export const version = '3.11.0';

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+
ignoreLocalVariables?: 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: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import { ReferenceTracker } from '@eslint-community/eslint-utils';
22
import { createRule } from '../utils/index.js';
33
import type { TSESTree } from '@typescript-eslint/types';
44
import { findVariable, isIn } from '../utils/ast-utils.js';
5-
import { getSvelteContext } from '../utils/svelte-context.js';
5+
import { getSvelteContext } from 'src/utils/svelte-context.js';
6+
import type { AST } from 'svelte-eslint-parser';
67

78
export default createRule('prefer-svelte-reactivity', {
89
meta: {
@@ -12,7 +13,18 @@ export default createRule('prefer-svelte-reactivity', {
1213
category: 'Possible Errors',
1314
recommended: true
1415
},
15-
schema: [],
16+
schema: [
17+
{
18+
type: 'object',
19+
properties: {
20+
ignoreLocalVariables: {
21+
type: 'boolean',
22+
default: true
23+
}
24+
},
25+
additionalProperties: false
26+
}
27+
],
1628
messages: {
1729
mutableDateUsed:
1830
'Found a mutable instance of the built-in Date class. Use SvelteDate instead.',
@@ -31,6 +43,7 @@ export default createRule('prefer-svelte-reactivity', {
3143
]
3244
},
3345
create(context) {
46+
const options = context.options[0] ?? { ignoreLocalVariables: true };
3447
const exportedVars: TSESTree.Node[] = [];
3548
return {
3649
...(getSvelteContext(context)?.svelteFileType === '.svelte.[js|ts]' && {
@@ -78,6 +91,10 @@ export default createRule('prefer-svelte-reactivity', {
7891
[ReferenceTracker.CONSTRUCT]: true
7992
}
8093
})) {
94+
if (options.ignoreLocalVariables && !isTopLevelDeclaration(node)) {
95+
continue;
96+
}
97+
8198
const messageId =
8299
path[0] === 'Date'
83100
? 'mutableDateUsed'
@@ -135,6 +152,32 @@ export default createRule('prefer-svelte-reactivity', {
135152
}
136153
});
137154

155+
function isTopLevelDeclaration(node: TSESTree.Node | AST.SvelteNode): boolean {
156+
let declaration: TSESTree.Node | AST.SvelteNode | null = node;
157+
while (
158+
declaration &&
159+
declaration.type !== 'VariableDeclaration' &&
160+
declaration.type !== 'FunctionDeclaration' &&
161+
declaration.type !== 'ClassDeclaration' &&
162+
declaration.type !== 'ExportDefaultDeclaration'
163+
) {
164+
declaration = declaration.parent as TSESTree.Node | AST.SvelteNode | null;
165+
}
166+
167+
if (!declaration) {
168+
return false;
169+
}
170+
171+
const parentType: string | undefined = declaration.parent?.type;
172+
return (
173+
parentType === 'SvelteScriptElement' ||
174+
parentType === 'Program' ||
175+
parentType === 'ExportDefaultDeclaration' ||
176+
parentType === 'ExportNamedDeclaration' ||
177+
parentType === 'ExportAllDeclaration'
178+
);
179+
}
180+
138181
function isDateMutable(referenceTracker: ReferenceTracker, ctorNode: TSESTree.Expression): boolean {
139182
return !referenceTracker
140183
.iteratePropertyReferences(ctorNode, {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"options": [
3+
{
4+
"ignoreLocalVariables": false
5+
}
6+
]
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
- message: Found a mutable instance of the built-in Map class. Use SvelteMap instead.
2+
line: 3
3+
column: 15
4+
suggestions: null
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<script>
2+
const foo = () => {
3+
const map = new Map();
4+
map.set('foo', 'bar');
5+
};
6+
</script>
7+
8+
{foo()}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<script>
2+
const foo = () => {
3+
const date = new Date();
4+
const map = new Map();
5+
const set = new Set();
6+
const url = new URL('https://svelte.dev/');
7+
const objectURL = URL.createObjectURL(new MediaSource());
8+
const urlSearchParams = new URLSearchParams();
9+
console.log({ date, map, set, url, objectURL, urlSearchParams });
10+
};
11+
</script>
12+
13+
{foo()}

0 commit comments

Comments
 (0)