Skip to content

Commit 8483bd6

Browse files
committed
feat: add autofixer to tell the LLM to check if some function called in effect is assigning state
1 parent 47fa0a4 commit 8483bd6

File tree

2 files changed

+57
-5
lines changed

2 files changed

+57
-5
lines changed

packages/mcp-server/src/mcp/autofixers/add-autofixers-issues.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,36 @@ describe('add_autofixers_issues', () => {
117117
);
118118
});
119119
});
120+
121+
it('should add a suggestion when calling a function inside an effect', () => {
122+
const content = run_autofixers_on_code(`
123+
<script>
124+
import { fetch_data } from './data.js';
125+
$effect(() => {
126+
fetch_data();
127+
});
128+
</script>`);
129+
130+
expect(content.suggestions.length).toBeGreaterThanOrEqual(1);
131+
expect(content.suggestions).toContain(
132+
`You are calling the function \`fetch_data\` inside an $effect. Please check if the function is reassigning a stateful variable because that's considered malpractice and check if it could use \`$derived\` instead. Ignore this suggestion if you are sure this function is not assigning any stateful variable or if you can't check if it does.`,
133+
);
134+
});
135+
136+
it('should add a suggestion when calling a function inside an effect (with non identifier callee)', () => {
137+
const content = run_autofixers_on_code(`
138+
<script>
139+
import { fetch_data } from './data.js';
140+
$effect(() => {
141+
fetch_data.fetch();
142+
});
143+
</script>`);
144+
145+
expect(content.suggestions.length).toBeGreaterThanOrEqual(1);
146+
expect(content.suggestions).toContain(
147+
`You are calling a function inside an $effect. Please check if the function is reassigning a stateful variable because that's considered malpractice and check if it could use \`$derived\` instead. Ignore this suggestion if you are sure this function is not assigning any stateful variable or if you can't check if it does.`,
148+
);
149+
});
120150
});
121151

122152
with_possible_inits('($init)', ({ init }) => {

packages/mcp-server/src/mcp/autofixers/visitors/assign-in-effect.ts

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
import type { AssignmentExpression, Identifier, Node, UpdateExpression } from 'estree';
1+
import type {
2+
AssignmentExpression,
3+
CallExpression,
4+
Identifier,
5+
Node,
6+
UpdateExpression,
7+
} from 'estree';
28
import type { Autofixer, AutofixerState } from './index.js';
39
import { left_most_id } from '../ast/utils.js';
410
import type { AST } from 'svelte-eslint-parser';
@@ -27,9 +33,9 @@ function run_if_in_effect(
2733
}
2834
}
2935

30-
function visitor(
36+
function assign_or_update_visitor(
3137
node: UpdateExpression | AssignmentExpression,
32-
{ state, path }: Context<Node | AST.SvelteNode, AutofixerState>,
38+
{ state, path, next }: Context<Node | AST.SvelteNode, AutofixerState>,
3339
) {
3440
run_if_in_effect(path, state, () => {
3541
function check_if_stateful_id(id: Identifier) {
@@ -58,9 +64,25 @@ function visitor(
5864
}
5965
}
6066
});
67+
next();
68+
}
69+
70+
function call_expression_visitor(
71+
node: CallExpression,
72+
{ state, path, next }: Context<Node | AST.SvelteNode, AutofixerState>,
73+
) {
74+
run_if_in_effect(path, state, () => {
75+
const function_name =
76+
node.callee.type === 'Identifier' ? `the function \`${node.callee.name}\`` : 'a function';
77+
state.output.suggestions.push(
78+
`You are calling ${function_name} inside an $effect. Please check if the function is reassigning a stateful variable because that's considered malpractice and check if it could use \`$derived\` instead. Ignore this suggestion if you are sure this function is not assigning any stateful variable or if you can't check if it does.`,
79+
);
80+
});
81+
next();
6182
}
6283

6384
export const assign_in_effect: Autofixer = {
64-
UpdateExpression: visitor,
65-
AssignmentExpression: visitor,
85+
UpdateExpression: assign_or_update_visitor,
86+
AssignmentExpression: assign_or_update_visitor,
87+
CallExpression: call_expression_visitor,
6688
};

0 commit comments

Comments
 (0)