From bf5f31c867a368bd14ddc66468c746807748f9d8 Mon Sep 17 00:00:00 2001 From: paoloricciuti Date: Fri, 10 Oct 2025 16:00:19 +0200 Subject: [PATCH 1/2] feat: `read_state_with_dollar` autofixer --- .changeset/proud-views-hang.md | 5 ++ .../autofixers/add-autofixers-issues.test.ts | 52 +++++++++++++++++++ .../src/mcp/autofixers/visitors/index.ts | 1 + .../visitors/read-state-with-dollar.ts | 22 ++++++++ .../visitors/suggest-attachments.ts | 1 - packages/mcp-server/src/parse/parse.ts | 12 ++++- 6 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 .changeset/proud-views-hang.md create mode 100644 packages/mcp-server/src/mcp/autofixers/visitors/read-state-with-dollar.ts diff --git a/.changeset/proud-views-hang.md b/.changeset/proud-views-hang.md new file mode 100644 index 0000000..546adee --- /dev/null +++ b/.changeset/proud-views-hang.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/mcp': patch +--- + +feat: `read_state_with_dollar` autofixer diff --git a/packages/mcp-server/src/mcp/autofixers/add-autofixers-issues.test.ts b/packages/mcp-server/src/mcp/autofixers/add-autofixers-issues.test.ts index 6e455ae..e0bf68d 100644 --- a/packages/mcp-server/src/mcp/autofixers/add-autofixers-issues.test.ts +++ b/packages/mcp-server/src/mcp/autofixers/add-autofixers-issues.test.ts @@ -687,4 +687,56 @@ describe('add_autofixers_issues', () => { }); }); }); + describe.only('read_state_with_dollar', () => { + with_possible_inits('($init)', ({ init }) => { + it(`should add an issue when reading a stateful variable initialized with ${init} like if it was a store`, () => { + const content = run_autofixers_on_code(` + `); + + expect(content.issues).toContain( + `You are reading the stateful variable "$x" with a "$" prefix. Stateful variables are not stores and should be read without the "$". Please read it as a normal variable "x"`, + ); + }); + }); + + it(`should not add an issue when reading an imported variable like if it was a store`, () => { + const content = run_autofixers_on_code(` + `); + + expect(content.issues).not.toContain( + `You are reading the stateful variable "$x" with a "$" prefix. Stateful variables are not stores and should be read without the "$". Please read it as a normal variable "x"`, + ); + }); + + it(`should not add an issue when reading a non-stateful variable like if it was a store`, () => { + const content = run_autofixers_on_code(` + `); + + expect(content.issues).not.toContain( + `You are reading the stateful variable "$x" with a "$" prefix. Stateful variables are not stores and should be read without the "$". Please read it as a normal variable "x"`, + ); + }); + + it(`should not add an issue when reading a prop like if it was a store`, () => { + const content = run_autofixers_on_code(` + `); + + expect(content.issues).not.toContain( + `You are reading the stateful variable "$x" with a "$" prefix. Stateful variables are not stores and should be read without the "$". Please read it as a normal variable "x"`, + ); + }); + }); }); diff --git a/packages/mcp-server/src/mcp/autofixers/visitors/index.ts b/packages/mcp-server/src/mcp/autofixers/visitors/index.ts index 6078f3b..9b3d0f6 100644 --- a/packages/mcp-server/src/mcp/autofixers/visitors/index.ts +++ b/packages/mcp-server/src/mcp/autofixers/visitors/index.ts @@ -17,3 +17,4 @@ export * from './imported-runes.js'; export * from './derived-with-function.js'; export * from './use-runes-instead-of-store.js'; export * from './suggest-attachments.js'; +export * from './read-state-with-dollar.js'; diff --git a/packages/mcp-server/src/mcp/autofixers/visitors/read-state-with-dollar.ts b/packages/mcp-server/src/mcp/autofixers/visitors/read-state-with-dollar.ts new file mode 100644 index 0000000..122bcdc --- /dev/null +++ b/packages/mcp-server/src/mcp/autofixers/visitors/read-state-with-dollar.ts @@ -0,0 +1,22 @@ +import type { Autofixer } from './index.js'; + +export const read_state_with_dollar: Autofixer = { + Identifier(node, { state }) { + if (node.name.startsWith('$')) { + const reference = state.parsed.find_reference_by_id(node); + if (reference && reference.resolved && reference.resolved.defs[0]?.node?.init) { + const is_state = state.parsed.is_rune(reference.resolved.defs[0].node.init, [ + '$state', + '$state.raw', + '$derived', + '$derived.by', + ]); + if (is_state) { + state.output.issues.push( + `You are reading the stateful variable "${node.name}" with a "$" prefix. Stateful variables are not stores and should be read without the "$". Please read it as a normal variable "${node.name.substring(1)}"`, + ); + } + } + } + }, +}; diff --git a/packages/mcp-server/src/mcp/autofixers/visitors/suggest-attachments.ts b/packages/mcp-server/src/mcp/autofixers/visitors/suggest-attachments.ts index 778b826..1d72ac0 100644 --- a/packages/mcp-server/src/mcp/autofixers/visitors/suggest-attachments.ts +++ b/packages/mcp-server/src/mcp/autofixers/visitors/suggest-attachments.ts @@ -25,7 +25,6 @@ export const suggest_attachments: Autofixer = { if (id) { const reference = state.parsed.find_reference_by_id(id); const definition = reference?.resolved?.defs[0]; - console.log(definition); if ( definition && (definition.type === 'Variable' || diff --git a/packages/mcp-server/src/parse/parse.ts b/packages/mcp-server/src/parse/parse.ts index 53fb9e1..01b814e 100644 --- a/packages/mcp-server/src/parse/parse.ts +++ b/packages/mcp-server/src/parse/parse.ts @@ -36,8 +36,18 @@ export function parse(code: string, file_path: string) { } return all_scopes; } + // walking the ast will also walk all the tokens if we don't remove them so we return them separately + // we also remove the parent which as a circular reference to the ast itself (and it's not needed since we use zimmerframe to walk the ast) + const { + ast: { tokens, ...ast }, + } = parsed; + + // @ts-expect-error we also have to delete it from the object or the circular reference remains + delete parsed.ast.tokens; + return { - ast: parsed.ast, + ast, + tokens, scope_manager: parsed.scopeManager as ScopeManager, visitor_keys: parsed.visitorKeys, get all_scopes() { From 33a0e17ee1cd564728ec7475ad03af181d16363f Mon Sep 17 00:00:00 2001 From: paoloricciuti Date: Fri, 10 Oct 2025 16:06:05 +0200 Subject: [PATCH 2/2] fix: remove `.only` duh --- .../mcp-server/src/mcp/autofixers/add-autofixers-issues.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mcp-server/src/mcp/autofixers/add-autofixers-issues.test.ts b/packages/mcp-server/src/mcp/autofixers/add-autofixers-issues.test.ts index e0bf68d..3caf593 100644 --- a/packages/mcp-server/src/mcp/autofixers/add-autofixers-issues.test.ts +++ b/packages/mcp-server/src/mcp/autofixers/add-autofixers-issues.test.ts @@ -687,7 +687,7 @@ describe('add_autofixers_issues', () => { }); }); }); - describe.only('read_state_with_dollar', () => { + describe('read_state_with_dollar', () => { with_possible_inits('($init)', ({ init }) => { it(`should add an issue when reading a stateful variable initialized with ${init} like if it was a store`, () => { const content = run_autofixers_on_code(`