@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 +124 -48
- package/dist/vue-service.d.ts +7 -2
- package/package.json +1 -1
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
|
|
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
|
|
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
|
-
|
|
19405
|
-
|
|
19406
|
-
|
|
19407
|
-
|
|
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
|
-
|
|
19420
|
-
|
|
19421
|
-
|
|
19422
|
-
|
|
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
|
|
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
|
-
|
|
19739
|
-
|
|
19740
|
-
|
|
19741
|
-
|
|
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
|
-
|
|
19779
|
-
|
|
19780
|
-
|
|
19781
|
-
|
|
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
|
-
|
|
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 =
|
|
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",
|
package/dist/vue-service.d.ts
CHANGED
|
@@ -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
|
|
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
|
/**
|