@treedy/vue-lsp-mcp 0.1.5 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -19382,46 +19382,71 @@ class StdioServerTransport {
19382
19382
  // src/index.ts
19383
19383
  import * as path3 from "path";
19384
19384
  import * as fs3 from "fs";
19385
- import { createRequire as createRequire2 } from "module";
19385
+ import { createRequire as createRequire3 } from "module";
19386
19386
 
19387
19387
  // src/vue-service.ts
19388
19388
  import * as path from "path";
19389
19389
  import * as fs from "fs";
19390
19390
  import { spawn } from "child_process";
19391
+ import { createRequire as createRequire2 } from "module";
19391
19392
  var documentContents = new Map;
19392
19393
  var documentVersions = new Map;
19393
19394
  var messageId = 0;
19395
+ var activeConnections = new Set;
19396
+ process.on("exit", () => {
19397
+ for (const conn of activeConnections) {
19398
+ try {
19399
+ conn.process.kill();
19400
+ if (conn.tsserver)
19401
+ conn.tsserver.kill();
19402
+ } catch (e) {}
19403
+ }
19404
+ });
19394
19405
  var connectionCache = new Map;
19395
19406
  function spawnTsServer(conn) {
19396
19407
  if (conn.tsserver)
19397
19408
  return;
19398
19409
  const projectRoot = conn.projectRoot;
19399
- const tsserverPaths = [
19400
- path.join(projectRoot, "node_modules", "typescript", "lib", "tsserver.js"),
19401
- path.join(projectRoot, "node_modules", ".bin", "tsserver")
19402
- ];
19410
+ const projectRequire = createRequire2(path.join(projectRoot, "package.json"));
19403
19411
  let tsserverPath = null;
19404
- for (const p of tsserverPaths) {
19405
- if (fs.existsSync(p)) {
19406
- tsserverPath = p;
19407
- break;
19412
+ try {
19413
+ const typescriptPath = projectRequire.resolve("typescript");
19414
+ tsserverPath = path.join(path.dirname(typescriptPath), "tsserver.js");
19415
+ } catch (e) {
19416
+ const commonPaths = [
19417
+ path.join(projectRoot, "node_modules", "typescript", "lib", "tsserver.js"),
19418
+ path.join(projectRoot, "node_modules", ".bin", "tsserver")
19419
+ ];
19420
+ for (const p of commonPaths) {
19421
+ if (fs.existsSync(p)) {
19422
+ tsserverPath = p;
19423
+ break;
19424
+ }
19408
19425
  }
19409
19426
  }
19410
- if (!tsserverPath) {
19427
+ if (!tsserverPath || !fs.existsSync(tsserverPath)) {
19411
19428
  console.error("[DEBUG] tsserver not found, Volar 3.x features may not work");
19412
19429
  return;
19413
19430
  }
19414
- const vuePluginPaths = [
19415
- path.join(projectRoot, "node_modules", "@vue", "typescript-plugin"),
19416
- path.join(projectRoot, "node_modules", "@vue", "language-core", "dist", "languagePlugin.js")
19417
- ];
19418
19431
  let vuePluginPath = null;
19419
- for (const p of vuePluginPaths) {
19420
- if (fs.existsSync(p)) {
19421
- vuePluginPath = p;
19422
- break;
19432
+ try {
19433
+ try {
19434
+ const pkgPath = projectRequire.resolve("@vue/typescript-plugin/package.json");
19435
+ vuePluginPath = path.dirname(pkgPath);
19436
+ } catch {
19437
+ const langCorePath = projectRequire.resolve("@vue/language-core");
19438
+ const commonPluginPaths = [
19439
+ path.join(projectRoot, "node_modules", "@vue", "typescript-plugin"),
19440
+ path.join(projectRoot, "node_modules", "@vue", "language-core", "dist", "languagePlugin.js")
19441
+ ];
19442
+ for (const p of commonPluginPaths) {
19443
+ if (fs.existsSync(p)) {
19444
+ vuePluginPath = p;
19445
+ break;
19446
+ }
19447
+ }
19423
19448
  }
19424
- }
19449
+ } catch (e) {}
19425
19450
  const plugins = vuePluginPath ? [{ name: vuePluginPath }] : [];
19426
19451
  conn.tsserver = spawn("node", [tsserverPath], {
19427
19452
  cwd: projectRoot,
@@ -19438,14 +19463,11 @@ function spawnTsServer(conn) {
19438
19463
  conn.tsserverBuffer += data.toString();
19439
19464
  parseTsServerMessages(conn);
19440
19465
  });
19441
- conn.tsserver.stderr?.on("data", (data) => {
19442
- console.error("[tsserver]", data.toString().trim());
19443
- });
19466
+ conn.tsserver.stderr?.on("data", (data) => {});
19444
19467
  conn.tsserver.on("error", (error2) => {
19445
19468
  console.error("[tsserver] Error:", error2);
19446
19469
  });
19447
19470
  conn.tsserver.on("exit", (code) => {
19448
- console.error(`[tsserver] Exited with code: ${code}`);
19449
19471
  conn.tsserver = undefined;
19450
19472
  });
19451
19473
  const seq = ++conn.tsserverSeq;
@@ -19730,18 +19752,30 @@ async function getConnection(projectRoot) {
19730
19752
  if (connectionCache.has(projectRoot)) {
19731
19753
  return connectionCache.get(projectRoot);
19732
19754
  }
19733
- const vueServerPaths = [
19734
- path.join(projectRoot, "node_modules", "@vue", "language-server", "bin", "vue-language-server.js"),
19735
- path.join(projectRoot, "node_modules", ".bin", "vue-language-server")
19736
- ];
19755
+ const projectRequire = createRequire2(path.join(projectRoot, "package.json"));
19737
19756
  let serverPath = null;
19738
- for (const p of vueServerPaths) {
19739
- if (fs.existsSync(p)) {
19740
- serverPath = p;
19741
- break;
19757
+ let args = [];
19758
+ try {
19759
+ const pkgPath = projectRequire.resolve("@vue/language-server/package.json");
19760
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
19761
+ const binPath = pkg.bin ? typeof pkg.bin === "string" ? pkg.bin : pkg.bin["vue-language-server"] : "bin/vue-language-server.js";
19762
+ serverPath = path.join(path.dirname(pkgPath), binPath);
19763
+ args = ["node", serverPath, "--stdio"];
19764
+ } catch (e) {
19765
+ const possiblePaths = [
19766
+ path.join(projectRoot, "node_modules", "@vue", "language-server", "bin", "vue-language-server.js")
19767
+ ];
19768
+ for (const p of possiblePaths) {
19769
+ if (fs.existsSync(p)) {
19770
+ serverPath = p;
19771
+ args = ["node", serverPath, "--stdio"];
19772
+ break;
19773
+ }
19774
+ }
19775
+ if (!serverPath) {
19776
+ args = ["npx", "@vue/language-server", "--stdio"];
19742
19777
  }
19743
19778
  }
19744
- const args = serverPath ? ["node", serverPath, "--stdio"] : ["npx", "@vue/language-server", "--stdio"];
19745
19779
  const proc = spawn(args[0], args.slice(1), {
19746
19780
  cwd: projectRoot,
19747
19781
  stdio: ["pipe", "pipe", "pipe"],
@@ -19756,29 +19790,34 @@ async function getConnection(projectRoot) {
19756
19790
  openedDocuments: new Set,
19757
19791
  projectRoot
19758
19792
  };
19793
+ activeConnections.add(conn);
19759
19794
  proc.stdout?.on("data", (data) => {
19760
19795
  conn.buffer += data.toString();
19761
19796
  parseMessages(conn);
19762
19797
  });
19763
- proc.stderr?.on("data", (data) => {
19764
- console.error("[vue-language-server]", data.toString());
19765
- });
19798
+ proc.stderr?.on("data", (data) => {});
19766
19799
  proc.on("error", (error2) => {
19767
19800
  console.error("Failed to start vue-language-server:", error2);
19768
19801
  });
19769
19802
  proc.on("exit", (code) => {
19770
19803
  connectionCache.delete(projectRoot);
19804
+ activeConnections.delete(conn);
19771
19805
  });
19772
19806
  connectionCache.set(projectRoot, conn);
19773
- const tsSdkPaths = [
19774
- path.join(projectRoot, "node_modules", "typescript", "lib"),
19775
- path.join(projectRoot, "node_modules", "typescript")
19776
- ];
19777
19807
  let tsSdkPath;
19778
- for (const p of tsSdkPaths) {
19779
- if (fs.existsSync(p)) {
19780
- tsSdkPath = p;
19781
- break;
19808
+ try {
19809
+ const tsPath = projectRequire.resolve("typescript");
19810
+ tsSdkPath = path.dirname(tsPath);
19811
+ } catch {
19812
+ const tsPaths = [
19813
+ path.join(projectRoot, "node_modules", "typescript", "lib"),
19814
+ path.join(projectRoot, "node_modules", "typescript")
19815
+ ];
19816
+ for (const p of tsPaths) {
19817
+ if (fs.existsSync(p)) {
19818
+ tsSdkPath = p;
19819
+ break;
19820
+ }
19782
19821
  }
19783
19822
  }
19784
19823
  await sendMessage(conn, "initialize", {
@@ -19792,7 +19831,13 @@ async function getConnection(projectRoot) {
19792
19831
  signatureHelp: {},
19793
19832
  definition: {},
19794
19833
  references: {},
19795
- publishDiagnostics: {}
19834
+ publishDiagnostics: {},
19835
+ synchronization: {
19836
+ didOpen: true,
19837
+ didChange: true,
19838
+ didSave: true,
19839
+ dynamicRegistration: true
19840
+ }
19796
19841
  },
19797
19842
  workspace: {
19798
19843
  configuration: true,
@@ -19836,10 +19881,26 @@ async function ensureDocumentOpen(conn, filePath) {
19836
19881
  });
19837
19882
  conn.openedDocuments.add(absPath);
19838
19883
  }
19839
- function updateDocument(filePath, content) {
19884
+ async function updateDocument(filePath, content) {
19840
19885
  const absPath = path.resolve(filePath);
19886
+ const projectRoot = findProjectRoot(filePath);
19841
19887
  documentContents.set(absPath, content);
19842
- documentVersions.set(absPath, (documentVersions.get(absPath) || 0) + 1);
19888
+ const newVersion = (documentVersions.get(absPath) || 0) + 1;
19889
+ documentVersions.set(absPath, newVersion);
19890
+ if (connectionCache.has(projectRoot)) {
19891
+ const conn = connectionCache.get(projectRoot);
19892
+ if (conn.openedDocuments.has(absPath)) {
19893
+ sendNotification(conn, "textDocument/didChange", {
19894
+ textDocument: {
19895
+ uri: toUri(absPath),
19896
+ version: newVersion
19897
+ },
19898
+ contentChanges: [
19899
+ { text: content }
19900
+ ]
19901
+ });
19902
+ }
19903
+ }
19843
19904
  }
19844
19905
  async function getQuickInfo(filePath, line, column) {
19845
19906
  const projectRoot = findProjectRoot(filePath);
@@ -20001,6 +20062,21 @@ async function getSignatureHelp(filePath, line, column) {
20001
20062
  async function getDiagnostics(filePath) {
20002
20063
  const projectRoot = findProjectRoot(filePath);
20003
20064
  const absPath = path.resolve(filePath);
20065
+ try {
20066
+ const conn = await getConnection(projectRoot);
20067
+ await ensureDocumentOpen(conn, absPath);
20068
+ let attempts = 0;
20069
+ while (attempts < 10) {
20070
+ if (conn.diagnosticsCache.has(absPath)) {
20071
+ return conn.diagnosticsCache.get(absPath);
20072
+ }
20073
+ await new Promise((r) => setTimeout(r, 200));
20074
+ attempts++;
20075
+ }
20076
+ return conn.diagnosticsCache.get(absPath) || [];
20077
+ } catch (e) {
20078
+ console.error("LSP diagnostics failed, falling back to vue-tsc spawn:", e);
20079
+ }
20004
20080
  const vueTscPaths = [
20005
20081
  path.join(projectRoot, "node_modules", ".bin", "vue-tsc"),
20006
20082
  path.join(projectRoot, "node_modules", "vue-tsc", "bin", "vue-tsc.js")
@@ -20547,7 +20623,7 @@ async function getSignatureHelp3(file, line, column) {
20547
20623
  }
20548
20624
  return getSignatureHelp2(file, line, column);
20549
20625
  }
20550
- var require2 = createRequire2(import.meta.url);
20626
+ var require2 = createRequire3(import.meta.url);
20551
20627
  var packageJson = require2("../package.json");
20552
20628
  var server = new McpServer({
20553
20629
  name: "vue-lsp-mcp",
@@ -20721,7 +20797,7 @@ server.tool("update_document", "Update Vue file content for incremental analysis
20721
20797
  content: exports_external.string().describe("New content for the file")
20722
20798
  }, async ({ file, content }) => {
20723
20799
  try {
20724
- updateDocument(file, content);
20800
+ await updateDocument(file, content);
20725
20801
  return {
20726
20802
  content: [{
20727
20803
  type: "text",
@@ -21,8 +21,9 @@ export declare function offsetToPosition(content: string, offset: number): {
21
21
  };
22
22
  /**
23
23
  * Update document content for incremental analysis
24
+ * Now properly synchronizes with LSP server
24
25
  */
25
- export declare function updateDocument(filePath: string, content: string): void;
26
+ export declare function updateDocument(filePath: string, content: string): Promise<void>;
26
27
  /**
27
28
  * Create Vue language service for a project
28
29
  */
@@ -91,7 +92,11 @@ interface Diagnostic {
91
92
  source?: string;
92
93
  }
93
94
  /**
94
- * Get diagnostics for a file using vue-tsc
95
+ * Get diagnostics for a file
96
+ *
97
+ * Strategy:
98
+ * 1. For single files, prefer LSP diagnostics (faster, incremental)
99
+ * 2. Fallback to vue-tsc only if LSP unavailable or for project-wide checks (if needed)
95
100
  */
96
101
  export declare function getDiagnostics(filePath: string): Promise<Diagnostic[]>;
97
102
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@treedy/vue-lsp-mcp",
3
- "version": "0.1.5",
3
+ "version": "0.2.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },