Skip to content

Commit 35335bc

Browse files
committed
feat(prefer-svelte-reactivity): reporting returned variables
1 parent 563bbc6 commit 35335bc

File tree

2 files changed

+55
-0
lines changed

2 files changed

+55
-0
lines changed

.changeset/lovely-moments-kneel.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): reporting returned variables

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

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ export default createRule('prefer-svelte-reactivity', {
3131
]
3232
},
3333
create(context) {
34+
const returnedVariables: Map<TSESTree.FunctionDeclaration, TSESTree.VariableDeclarator[]> =
35+
new Map();
3436
const exportedVars: TSESTree.Node[] = [];
3537
return {
3638
...(getSvelteContext(context)?.svelteFileType === '.svelte.[js|ts]' && {
@@ -59,6 +61,28 @@ export default createRule('prefer-svelte-reactivity', {
5961
}
6062
}
6163
}),
64+
Identifier(node) {
65+
const enclosingReturn = findEnclosingReturn(node);
66+
if (enclosingReturn === null) {
67+
return;
68+
}
69+
const enclosingFunction = findEnclosingFunction(enclosingReturn);
70+
if (enclosingFunction === null) {
71+
return;
72+
}
73+
const variable = findVariable(context, node);
74+
if (
75+
variable === null ||
76+
variable.identifiers.length < 1 ||
77+
variable.identifiers[0].parent.type !== 'VariableDeclarator'
78+
) {
79+
return;
80+
}
81+
if (!returnedVariables.has(enclosingFunction)) {
82+
returnedVariables.set(enclosingFunction, []);
83+
}
84+
returnedVariables.get(enclosingFunction)?.push(variable.identifiers[0].parent);
85+
},
6286
'Program:exit'() {
6387
const referenceTracker = new ReferenceTracker(context.sourceCode.scopeManager.globalScope!);
6488
for (const { node, path } of referenceTracker.iterateGlobalReferences({
@@ -96,6 +120,14 @@ export default createRule('prefer-svelte-reactivity', {
96120
});
97121
}
98122
}
123+
for (const returnedVar of Array.from(returnedVariables.values()).flat()) {
124+
if (isIn(node, returnedVar)) {
125+
context.report({
126+
messageId,
127+
node
128+
});
129+
}
130+
}
99131
if (path[0] === 'Date' && isDateMutable(referenceTracker, node as TSESTree.Expression)) {
100132
context.report({
101133
messageId: 'mutableDateUsed',
@@ -135,6 +167,24 @@ export default createRule('prefer-svelte-reactivity', {
135167
}
136168
});
137169

170+
function findAncestorOfType<T>(node: TSESTree.Node, type: T): (TSESTree.Node & { type: T }) | null {
171+
if (node.type === type) {
172+
return node as TSESTree.Node & { type: T };
173+
}
174+
if (node.parent === undefined || node.parent === null) {
175+
return null;
176+
}
177+
return findAncestorOfType(node.parent, type);
178+
}
179+
180+
function findEnclosingReturn(node: TSESTree.Node): TSESTree.ReturnStatement | null {
181+
return findAncestorOfType(node, 'ReturnStatement');
182+
}
183+
184+
function findEnclosingFunction(node: TSESTree.Node): TSESTree.FunctionDeclaration | null {
185+
return findAncestorOfType(node, 'FunctionDeclaration');
186+
}
187+
138188
function isDateMutable(referenceTracker: ReferenceTracker, ctorNode: TSESTree.Expression): boolean {
139189
return !referenceTracker
140190
.iteratePropertyReferences(ctorNode, {

0 commit comments

Comments
 (0)