Skip to content

Commit c2826b3

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

File tree

2 files changed

+61
-0
lines changed

2 files changed

+61
-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: 56 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,20 @@ 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+
}
131+
if (findEnclosingReturn(node) !== null) {
132+
context.report({
133+
messageId,
134+
node
135+
});
136+
}
99137
if (path[0] === 'Date' && isDateMutable(referenceTracker, node as TSESTree.Expression)) {
100138
context.report({
101139
messageId: 'mutableDateUsed',
@@ -135,6 +173,24 @@ export default createRule('prefer-svelte-reactivity', {
135173
}
136174
});
137175

176+
function findAncestorOfType<T>(node: TSESTree.Node, type: T): (TSESTree.Node & { type: T }) | null {
177+
if (node.type === type) {
178+
return node as TSESTree.Node & { type: T };
179+
}
180+
if (node.parent === undefined || node.parent === null) {
181+
return null;
182+
}
183+
return findAncestorOfType(node.parent, type);
184+
}
185+
186+
function findEnclosingReturn(node: TSESTree.Node): TSESTree.ReturnStatement | null {
187+
return findAncestorOfType(node, 'ReturnStatement');
188+
}
189+
190+
function findEnclosingFunction(node: TSESTree.Node): TSESTree.FunctionDeclaration | null {
191+
return findAncestorOfType(node, 'FunctionDeclaration');
192+
}
193+
138194
function isDateMutable(referenceTracker: ReferenceTracker, ctorNode: TSESTree.Expression): boolean {
139195
return !referenceTracker
140196
.iteratePropertyReferences(ctorNode, {

0 commit comments

Comments
 (0)