fix(lsp): use Node.js child_process on Windows to avoid Bun spawn segfault
Bun has unfixed segfault issues on Windows when spawning subprocesses (oven-sh/bun#25798, #26026, #23043). Even upgrading to Bun v1.3.6+ does not resolve the crashes. Instead of blocking LSP on Windows with version checks, use Node.js child_process.spawn as fallback. This allows LSP to work on Windows regardless of Bun version. Changes: - Add UnifiedProcess interface bridging Bun Subprocess and Node ChildProcess - Use Node.js spawn on Windows, Bun spawn on other platforms - Add CWD validation before spawn to prevent libuv null dereference - Add binary existence pre-check on Windows with helpful error messages - Enable shell: true for Node spawn on Windows for .cmd/.bat resolution - Remove ineffective Bun version blocking (v1.3.5 check) - Add tests for CWD validation and start() error handling Closes #1047 Ref: oven-sh/bun#25798
This commit is contained in:
@@ -12,7 +12,7 @@ mock.module("vscode-jsonrpc/node", () => ({
|
||||
StreamMessageWriter: function StreamMessageWriter() {},
|
||||
}))
|
||||
|
||||
import { LSPClient } from "./client"
|
||||
import { LSPClient, validateCwd } from "./client"
|
||||
import type { ResolvedServer } from "./types"
|
||||
|
||||
describe("LSPClient", () => {
|
||||
@@ -60,4 +60,91 @@ describe("LSPClient", () => {
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe("validateCwd", () => {
|
||||
it("returns valid for existing directory", () => {
|
||||
// #given
|
||||
const dir = mkdtempSync(join(tmpdir(), "lsp-cwd-test-"))
|
||||
|
||||
try {
|
||||
// #when
|
||||
const result = validateCwd(dir)
|
||||
|
||||
// #then
|
||||
expect(result.valid).toBe(true)
|
||||
expect(result.error).toBeUndefined()
|
||||
} finally {
|
||||
rmSync(dir, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
it("returns invalid for non-existent directory", () => {
|
||||
// #given
|
||||
const nonExistentDir = join(tmpdir(), "lsp-cwd-nonexistent-" + Date.now())
|
||||
|
||||
// #when
|
||||
const result = validateCwd(nonExistentDir)
|
||||
|
||||
// #then
|
||||
expect(result.valid).toBe(false)
|
||||
expect(result.error).toContain("Working directory does not exist")
|
||||
})
|
||||
|
||||
it("returns invalid when path is a file", () => {
|
||||
// #given
|
||||
const dir = mkdtempSync(join(tmpdir(), "lsp-cwd-file-test-"))
|
||||
const filePath = join(dir, "not-a-dir.txt")
|
||||
writeFileSync(filePath, "test content")
|
||||
|
||||
try {
|
||||
// #when
|
||||
const result = validateCwd(filePath)
|
||||
|
||||
// #then
|
||||
expect(result.valid).toBe(false)
|
||||
expect(result.error).toContain("Path is not a directory")
|
||||
} finally {
|
||||
rmSync(dir, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe("start", () => {
|
||||
it("throws error when working directory does not exist", async () => {
|
||||
// #given
|
||||
const nonExistentDir = join(tmpdir(), "lsp-test-nonexistent-" + Date.now())
|
||||
const server: ResolvedServer = {
|
||||
id: "typescript",
|
||||
command: ["typescript-language-server", "--stdio"],
|
||||
extensions: [".ts"],
|
||||
priority: 0,
|
||||
}
|
||||
const client = new LSPClient(nonExistentDir, server)
|
||||
|
||||
// #when / #then
|
||||
await expect(client.start()).rejects.toThrow("Working directory does not exist")
|
||||
})
|
||||
|
||||
it("throws error when path is a file instead of directory", async () => {
|
||||
// #given
|
||||
const dir = mkdtempSync(join(tmpdir(), "lsp-client-test-"))
|
||||
const filePath = join(dir, "not-a-dir.txt")
|
||||
writeFileSync(filePath, "test content")
|
||||
|
||||
const server: ResolvedServer = {
|
||||
id: "typescript",
|
||||
command: ["typescript-language-server", "--stdio"],
|
||||
extensions: [".ts"],
|
||||
priority: 0,
|
||||
}
|
||||
const client = new LSPClient(filePath, server)
|
||||
|
||||
try {
|
||||
// #when / #then
|
||||
await expect(client.start()).rejects.toThrow("Path is not a directory")
|
||||
} finally {
|
||||
rmSync(dir, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user