Skip to content

Commit 1c6c0a9

Browse files
Merge pull request #78 from sveltejs/invalid-css-autofixer
feat: suggest against js variables in css
2 parents ed25933 + a321244 commit 1c6c0a9

File tree

3 files changed

+103
-0
lines changed

3 files changed

+103
-0
lines changed

.changeset/seven-breads-stay.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@sveltejs/mcp': patch
3+
---
4+
5+
feat: suggest against js variables in css
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import { beforeEach, describe, expect, it } from 'vitest';
2+
import { server } from '../../index.js';
3+
4+
/**
5+
* Small utility to create a JSON-RPC request without having to always specify as const
6+
*/
7+
function request<const T>(request: T) {
8+
return request;
9+
}
10+
11+
async function autofixer_tool_call(code: string, is_error = false, desired_svelte_version = 5) {
12+
const result = await server.receive({
13+
jsonrpc: '2.0',
14+
id: 2,
15+
method: 'tools/call',
16+
params: {
17+
name: 'svelte-autofixer',
18+
arguments: {
19+
code,
20+
desired_svelte_version,
21+
filename: 'App.svelte',
22+
},
23+
},
24+
});
25+
26+
expect(result).toBeDefined();
27+
expect(result.result).toBeDefined();
28+
if (is_error) {
29+
return result.result;
30+
}
31+
expect(result.result.structuredContent).toBeDefined();
32+
return result.result.structuredContent;
33+
}
34+
35+
describe('svelte-autofixer tool', () => {
36+
beforeEach(async () => {
37+
const initialize_request = request({
38+
jsonrpc: '2.0',
39+
id: 1,
40+
method: 'initialize',
41+
params: {
42+
protocolVersion: '2025-06-18',
43+
capabilities: {
44+
roots: { listChanged: true },
45+
},
46+
clientInfo: {
47+
name: 'test-client',
48+
version: '1.0.0',
49+
},
50+
},
51+
});
52+
53+
await server.receive(initialize_request, {
54+
sessionId: 'svelte-autofixer-session',
55+
});
56+
});
57+
58+
it('should add suggestions for js parse errors', async () => {
59+
const content = await autofixer_tool_call(`<script>
60+
$state count = 0;
61+
</script>`);
62+
expect(content.issues.length).toBeGreaterThan(0);
63+
expect(content.suggestions).toContain(
64+
"The code can't be compiled because a Javascript parse error. In case you are using runes like this `$state variable_name = 3;` or `$derived variable_name = 3 * count` that's not how runes are used. You need to use them as function calls without importing them: `const variable_name = $state(3)` and `const variable_name = $derived(3 * count)`.",
65+
);
66+
});
67+
68+
it('should add suggestions for css invalid identifier', async () => {
69+
const content = await autofixer_tool_call(`<script>
70+
let my_color = $state('red');
71+
</script>
72+
73+
<style>
74+
.my-class {
75+
color: {my_color};
76+
}
77+
</style>`);
78+
79+
expect(content.issues.length).toBeGreaterThan(0);
80+
expect(content.suggestions).toContain(
81+
"The code can't be compiled because a valid CSS identifier is expected. This sometimes means you are trying to use a variable in CSS like this: `color: {my_color}` but Svelte doesn't support that. You can use inline CSS variables for that `<div style:--color={my_color}></div>` and then use the variable as usual in CSS with `color: var(--color)`.",
82+
);
83+
});
84+
85+
it('should error in case the passed in version is different from 4 or 5', async () => {
86+
const content = await autofixer_tool_call(`whatever`, true, 3);
87+
88+
expect(content.content).toBeDefined();
89+
expect(content.content[0]).toBeDefined();
90+
expect(content.content[0].text).toContain(
91+
'The desired_svelte_version MUST be either 4 or 5 but received "3"',
92+
);
93+
});
94+
});

packages/mcp-server/src/mcp/handlers/tools/svelte-autofixer.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,10 @@ export function svelte_autofixer(server: SvelteMcp) {
9090
content.suggestions.push(
9191
"The code can't be compiled because a Javascript parse error. In case you are using runes like this `$state variable_name = 3;` or `$derived variable_name = 3 * count` that's not how runes are used. You need to use them as function calls without importing them: `const variable_name = $state(3)` and `const variable_name = $derived(3 * count)`.",
9292
);
93+
} else if (error.message.includes('css_expected_identifier')) {
94+
content.suggestions.push(
95+
"The code can't be compiled because a valid CSS identifier is expected. This sometimes means you are trying to use a variable in CSS like this: `color: {my_color}` but Svelte doesn't support that. You can use inline CSS variables for that `<div style:--color={my_color}></div>` and then use the variable as usual in CSS with `color: var(--color)`.",
96+
);
9397
}
9498
}
9599

0 commit comments

Comments
 (0)