Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import type { AssignmentExpression, Identifier, Node, UpdateExpression } from 'estree';
import type { Autofixer, AutofixerState } from '.';
import type { Autofixer, AutofixerState } from './index.js';
import { left_most_id } from '../ast/utils.js';
import type { SvelteNode } from 'svelte-eslint-parser/lib/ast';
import type { AST } from 'svelte-eslint-parser';
import type { Context } from 'zimmerframe';

function run_if_in_effect(path: (Node | SvelteNode)[], state: AutofixerState, to_run: () => void) {
function run_if_in_effect(
path: (Node | AST.SvelteNode)[],
state: AutofixerState,
to_run: () => void,
) {
const in_effect = path.findLast(
(node) =>
node.type === 'CallExpression' &&
Expand All @@ -25,7 +29,7 @@ function run_if_in_effect(path: (Node | SvelteNode)[], state: AutofixerState, to

function visitor(
node: UpdateExpression | AssignmentExpression,
{ state, path }: Context<Node | SvelteNode, AutofixerState>,
{ state, path }: Context<Node | AST.SvelteNode, AutofixerState>,
) {
run_if_in_effect(path, state, () => {
function check_if_stateful_id(id: Identifier) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
import type { Identifier, PrivateIdentifier } from 'estree';
import type { Autofixer } from '.';
import type { Autofixer } from './index.js';

export const derived_with_function: Autofixer = {
CallExpression(node, { state, path }) {
if (
node.callee.type === 'Identifier' &&
node.callee.name === '$derived' &&
state.parsed.is_rune(node, ['$derived']) &&
(node.arguments[0].type === 'ArrowFunctionExpression' ||
node.arguments[0].type === 'FunctionExpression')
(node.arguments[0]?.type === 'ArrowFunctionExpression' ||
node.arguments[0]?.type === 'FunctionExpression')
) {
const parent = path[path.length - 1];
let variable_id: Identifier | PrivateIdentifier | undefined;
if (parent.type === 'VariableDeclarator' && parent.id.type === 'Identifier') {
if (parent?.type === 'VariableDeclarator' && parent.id.type === 'Identifier') {
// const something = $derived(...)
variable_id = parent.id;
} else if (parent.type === 'PropertyDefinition') {
} else if (parent?.type === 'PropertyDefinition') {
// class X { something = $derived(...) }
variable_id =
parent.key.type === 'Identifier'
? parent.key
: parent.key.type === 'PrivateIdentifier'
? parent.key
: undefined;
} else if (parent.type === 'AssignmentExpression') {
} else if (parent?.type === 'AssignmentExpression') {
// this.something = $derived(...)
variable_id =
parent.left.type === 'MemberExpression'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { base_runes } from '../../../constants.js';
import type { Autofixer } from '.';
import type { Autofixer } from './index.js';

const dollarless_runes = base_runes.map((r) => r.replace('$', ''));

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Autofixer } from '.';
import type { Autofixer } from './index.js';
import { left_most_id } from '../ast/utils.js';

const UPDATE_PROPERTIES = ['set', 'update'];
Expand All @@ -7,7 +7,7 @@ export const set_or_update_state: Autofixer = {
MemberExpression(node, { state, next, path }) {
const parent = path[path.length - 1];
if (
parent.type === 'CallExpression' &&
parent?.type === 'CallExpression' &&
parent.callee === node &&
node.property.type === 'Identifier' &&
UPDATE_PROPERTIES.includes(node.property.name)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Autofixer } from '.';
import type { Autofixer } from './index.js';

export const use_runes_instead_of_store: Autofixer = {
ImportDeclaration(node, { state, next }) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ async function compress_and_encode_text(input: string) {
} else {
for (let i = 0; i < value.length; i++) {
// decoding as utf-8 will make btoa reject the string
buffer += String.fromCharCode(value[i]);
buffer += String.fromCharCode(value[i]!);
}
}
}
Expand Down
16 changes: 16 additions & 0 deletions packages/mcp-stdio/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# @sveltejs/mcp

The CLI version of the Svelte MCP.

You can run it directly with

```bash
npx @sveltejs/mcp
```

or install it and then run it

```bash
pnpm i @sveltejs/mcp
pnpm svelte-mcp
```
43 changes: 43 additions & 0 deletions packages/mcp-stdio/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"name": "@sveltejs/mcp",
"version": "0.0.1",
"type": "module",
"license": "MIT",
"homepage": "https://github.com/sveltejs/mcp#readme",
"bugs": {
"url": "https://github.com/sveltejs/mcp/issues"
},
"bin": {
"svelte-mcp": "./dist/index.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/sveltejs/mcp.git"
},
"author": "Author Name <[email protected]>",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needs an update?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ugh yeah i meant to remove it

"files": [
"dist"
],
"main": "./dist/index.js",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer exports map over main

main and bin being the same seems odd as well. does this export anything or is it cli only? maybe just remove main and not have an exports map?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uh I was sure i removed it...fixing now

"publishConfig": {
"access": "public"
},
"scripts": {
"build": "tsdown",
"dev": "tsdown --watch",
"test": "vitest",
"check": "tsc --noEmit"
},
"devDependencies": {
"@sveltejs/mcp-server": "workspace:^",
"@tmcp/transport-stdio": "^0.3.0",
"@types/node": "^22.15.17",
"publint": "^0.3.13",
"tsdown": "^0.11.9",
"typescript": "^5.8.3",
"vitest": "^3.1.3"
},
"dependencies": {
"eslint": "^9.36.0"
}
}
7 changes: 7 additions & 0 deletions packages/mcp-stdio/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#! /usr/bin/env node
import { server } from '@sveltejs/mcp-server';
import { StdioTransport } from '@tmcp/transport-stdio';

const transport = new StdioTransport(server);

transport.listen();
5 changes: 5 additions & 0 deletions packages/mcp-stdio/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"extends": "../../tsconfig.json",
"include": ["src"],
"exclude": ["node_modules", "dist"]
}
21 changes: 21 additions & 0 deletions packages/mcp-stdio/tsdown.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { defineConfig } from 'tsdown';

export default defineConfig([
{
entry: ['./src/index.ts'],
platform: 'node',
define: {
// some eslint-plugin-svelte code expects __filename to exists but in an ESM environment it does not.
__filename: '""',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doesn't that risk breaking stuff if something actually uses __filename ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked and it doesn't seem to be used by anything we use really...maybe we can define it with the actual current filename using import.meta.url?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually scratch that we can replace it with import.meta.filename

},
// we need eslint at runtime but the bundler doesn't bundle `require`'s which `eslint-plugin-svelte` uses to require
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where does eslint-plugin-svelte come from? i don't see it in dependencies

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a dependency of @sveltejs/mcp-server

// `eslint/use-at-your-own-risk`. If we didn't have `eslint` as an actual dependency and didn't externalize it
// the require would fail once executed in a project without eslint installed.
external: ['eslint'],
publint: true,
dts: false,
treeshake: true,
clean: true,
target: 'esnext',
},
]);
3 changes: 3 additions & 0 deletions packages/mcp-stdio/vitest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { defineConfig } from 'vitest/config';

export default defineConfig({});
Loading