Skip to content

Commit d366a14

Browse files
authored
refactor: migrate src/lsp/server.ts from Bun.file()/Bun.write() to Filesystem module (anomalyco#14138)
1 parent cfea5c7 commit d366a14

File tree

4 files changed

+179
-38
lines changed

4 files changed

+179
-38
lines changed

packages/opencode/src/lsp/client.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,7 @@ export namespace LSPClient {
147147
notify: {
148148
async open(input: { path: string }) {
149149
input.path = path.isAbsolute(input.path) ? input.path : path.resolve(Instance.directory, input.path)
150-
const file = Bun.file(input.path)
151-
const text = await file.text()
150+
const text = await Filesystem.readText(input.path)
152151
const extension = path.extname(input.path)
153152
const languageId = LANGUAGE_EXTENSIONS[extension] ?? "plaintext"
154153

packages/opencode/src/lsp/server.ts

Lines changed: 34 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ export namespace LSPServer {
131131
"bin",
132132
"vue-language-server.js",
133133
)
134-
if (!(await Bun.file(js).exists())) {
134+
if (!(await Filesystem.exists(js))) {
135135
if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return
136136
await Bun.spawn([BunProc.which(), "install", "@vue/language-server"], {
137137
cwd: Global.Path.bin,
@@ -173,14 +173,14 @@ export namespace LSPServer {
173173
if (!eslint) return
174174
log.info("spawning eslint server")
175175
const serverPath = path.join(Global.Path.bin, "vscode-eslint", "server", "out", "eslintServer.js")
176-
if (!(await Bun.file(serverPath).exists())) {
176+
if (!(await Filesystem.exists(serverPath))) {
177177
if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return
178178
log.info("downloading and building VS Code ESLint server")
179179
const response = await fetch("https://github.com/microsoft/vscode-eslint/archive/refs/heads/main.zip")
180180
if (!response.ok) return
181181

182182
const zipPath = path.join(Global.Path.bin, "vscode-eslint.zip")
183-
await Bun.file(zipPath).write(response)
183+
if (response.body) await Filesystem.writeStream(zipPath, response.body)
184184

185185
const ok = await Archive.extractZip(zipPath, Global.Path.bin)
186186
.then(() => true)
@@ -242,7 +242,7 @@ export namespace LSPServer {
242242

243243
const resolveBin = async (target: string) => {
244244
const localBin = path.join(root, target)
245-
if (await Bun.file(localBin).exists()) return localBin
245+
if (await Filesystem.exists(localBin)) return localBin
246246

247247
const candidates = Filesystem.up({
248248
targets: [target],
@@ -326,7 +326,7 @@ export namespace LSPServer {
326326
async spawn(root) {
327327
const localBin = path.join(root, "node_modules", ".bin", "biome")
328328
let bin: string | undefined
329-
if (await Bun.file(localBin).exists()) bin = localBin
329+
if (await Filesystem.exists(localBin)) bin = localBin
330330
if (!bin) {
331331
const found = Bun.which("biome")
332332
if (found) bin = found
@@ -467,7 +467,7 @@ export namespace LSPServer {
467467
const potentialPythonPath = isWindows
468468
? path.join(venvPath, "Scripts", "python.exe")
469469
: path.join(venvPath, "bin", "python")
470-
if (await Bun.file(potentialPythonPath).exists()) {
470+
if (await Filesystem.exists(potentialPythonPath)) {
471471
initialization["pythonPath"] = potentialPythonPath
472472
break
473473
}
@@ -479,7 +479,7 @@ export namespace LSPServer {
479479
const potentialTyPath = isWindows
480480
? path.join(venvPath, "Scripts", "ty.exe")
481481
: path.join(venvPath, "bin", "ty")
482-
if (await Bun.file(potentialTyPath).exists()) {
482+
if (await Filesystem.exists(potentialTyPath)) {
483483
binary = potentialTyPath
484484
break
485485
}
@@ -511,7 +511,7 @@ export namespace LSPServer {
511511
const args = []
512512
if (!binary) {
513513
const js = path.join(Global.Path.bin, "node_modules", "pyright", "dist", "pyright-langserver.js")
514-
if (!(await Bun.file(js).exists())) {
514+
if (!(await Filesystem.exists(js))) {
515515
if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return
516516
await Bun.spawn([BunProc.which(), "install", "pyright"], {
517517
cwd: Global.Path.bin,
@@ -536,7 +536,7 @@ export namespace LSPServer {
536536
const potentialPythonPath = isWindows
537537
? path.join(venvPath, "Scripts", "python.exe")
538538
: path.join(venvPath, "bin", "python")
539-
if (await Bun.file(potentialPythonPath).exists()) {
539+
if (await Filesystem.exists(potentialPythonPath)) {
540540
initialization["pythonPath"] = potentialPythonPath
541541
break
542542
}
@@ -571,7 +571,7 @@ export namespace LSPServer {
571571
process.platform === "win32" ? "language_server.bat" : "language_server.sh",
572572
)
573573

574-
if (!(await Bun.file(binary).exists())) {
574+
if (!(await Filesystem.exists(binary))) {
575575
const elixir = Bun.which("elixir")
576576
if (!elixir) {
577577
log.error("elixir is required to run elixir-ls")
@@ -584,7 +584,7 @@ export namespace LSPServer {
584584
const response = await fetch("https://github.com/elixir-lsp/elixir-ls/archive/refs/heads/master.zip")
585585
if (!response.ok) return
586586
const zipPath = path.join(Global.Path.bin, "elixir-ls.zip")
587-
await Bun.file(zipPath).write(response)
587+
if (response.body) await Filesystem.writeStream(zipPath, response.body)
588588

589589
const ok = await Archive.extractZip(zipPath, Global.Path.bin)
590590
.then(() => true)
@@ -692,7 +692,7 @@ export namespace LSPServer {
692692
}
693693

694694
const tempPath = path.join(Global.Path.bin, assetName)
695-
await Bun.file(tempPath).write(downloadResponse)
695+
if (downloadResponse.body) await Filesystem.writeStream(tempPath, downloadResponse.body)
696696

697697
if (ext === "zip") {
698698
const ok = await Archive.extractZip(tempPath, Global.Path.bin)
@@ -710,7 +710,7 @@ export namespace LSPServer {
710710

711711
bin = path.join(Global.Path.bin, "zls" + (platform === "win32" ? ".exe" : ""))
712712

713-
if (!(await Bun.file(bin).exists())) {
713+
if (!(await Filesystem.exists(bin))) {
714714
log.error("Failed to extract zls binary")
715715
return
716716
}
@@ -857,7 +857,7 @@ export namespace LSPServer {
857857
// Stop at filesystem root
858858
const cargoTomlPath = path.join(currentDir, "Cargo.toml")
859859
try {
860-
const cargoTomlContent = await Bun.file(cargoTomlPath).text()
860+
const cargoTomlContent = await Filesystem.readText(cargoTomlPath)
861861
if (cargoTomlContent.includes("[workspace]")) {
862862
return currentDir
863863
}
@@ -907,7 +907,7 @@ export namespace LSPServer {
907907

908908
const ext = process.platform === "win32" ? ".exe" : ""
909909
const direct = path.join(Global.Path.bin, "clangd" + ext)
910-
if (await Bun.file(direct).exists()) {
910+
if (await Filesystem.exists(direct)) {
911911
return {
912912
process: spawn(direct, args, {
913913
cwd: root,
@@ -920,7 +920,7 @@ export namespace LSPServer {
920920
if (!entry.isDirectory()) continue
921921
if (!entry.name.startsWith("clangd_")) continue
922922
const candidate = path.join(Global.Path.bin, entry.name, "bin", "clangd" + ext)
923-
if (await Bun.file(candidate).exists()) {
923+
if (await Filesystem.exists(candidate)) {
924924
return {
925925
process: spawn(candidate, args, {
926926
cwd: root,
@@ -990,7 +990,7 @@ export namespace LSPServer {
990990
log.error("Failed to write clangd archive")
991991
return
992992
}
993-
await Bun.write(archive, buf)
993+
await Filesystem.write(archive, Buffer.from(buf))
994994

995995
const zip = name.endsWith(".zip")
996996
const tar = name.endsWith(".tar.xz")
@@ -1014,7 +1014,7 @@ export namespace LSPServer {
10141014
await fs.rm(archive, { force: true })
10151015

10161016
const bin = path.join(Global.Path.bin, "clangd_" + tag, "bin", "clangd" + ext)
1017-
if (!(await Bun.file(bin).exists())) {
1017+
if (!(await Filesystem.exists(bin))) {
10181018
log.error("Failed to extract clangd binary")
10191019
return
10201020
}
@@ -1045,7 +1045,7 @@ export namespace LSPServer {
10451045
const args: string[] = []
10461046
if (!binary) {
10471047
const js = path.join(Global.Path.bin, "node_modules", "svelte-language-server", "bin", "server.js")
1048-
if (!(await Bun.file(js).exists())) {
1048+
if (!(await Filesystem.exists(js))) {
10491049
if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return
10501050
await Bun.spawn([BunProc.which(), "install", "svelte-language-server"], {
10511051
cwd: Global.Path.bin,
@@ -1092,7 +1092,7 @@ export namespace LSPServer {
10921092
const args: string[] = []
10931093
if (!binary) {
10941094
const js = path.join(Global.Path.bin, "node_modules", "@astrojs", "language-server", "bin", "nodeServer.js")
1095-
if (!(await Bun.file(js).exists())) {
1095+
if (!(await Filesystem.exists(js))) {
10961096
if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return
10971097
await Bun.spawn([BunProc.which(), "install", "@astrojs/language-server"], {
10981098
cwd: Global.Path.bin,
@@ -1248,7 +1248,7 @@ export namespace LSPServer {
12481248
const distPath = path.join(Global.Path.bin, "kotlin-ls")
12491249
const launcherScript =
12501250
process.platform === "win32" ? path.join(distPath, "kotlin-lsp.cmd") : path.join(distPath, "kotlin-lsp.sh")
1251-
const installed = await Bun.file(launcherScript).exists()
1251+
const installed = await Filesystem.exists(launcherScript)
12521252
if (!installed) {
12531253
if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return
12541254
log.info("Downloading Kotlin Language Server from GitHub.")
@@ -1307,7 +1307,7 @@ export namespace LSPServer {
13071307
}
13081308
log.info("Installed Kotlin Language Server", { path: launcherScript })
13091309
}
1310-
if (!(await Bun.file(launcherScript).exists())) {
1310+
if (!(await Filesystem.exists(launcherScript))) {
13111311
log.error(`Failed to locate the Kotlin LS launcher script in the installed directory: ${distPath}.`)
13121312
return
13131313
}
@@ -1336,7 +1336,7 @@ export namespace LSPServer {
13361336
"src",
13371337
"server.js",
13381338
)
1339-
const exists = await Bun.file(js).exists()
1339+
const exists = await Filesystem.exists(js)
13401340
if (!exists) {
13411341
if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return
13421342
await Bun.spawn([BunProc.which(), "install", "yaml-language-server"], {
@@ -1443,7 +1443,7 @@ export namespace LSPServer {
14431443
}
14441444

14451445
const tempPath = path.join(Global.Path.bin, assetName)
1446-
await Bun.file(tempPath).write(downloadResponse)
1446+
if (downloadResponse.body) await Filesystem.writeStream(tempPath, downloadResponse.body)
14471447

14481448
// Unlike zls which is a single self-contained binary,
14491449
// lua-language-server needs supporting files (meta/, locale/, etc.)
@@ -1482,7 +1482,7 @@ export namespace LSPServer {
14821482
// Binary is located in bin/ subdirectory within the extracted archive
14831483
bin = path.join(installDir, "bin", "lua-language-server" + (platform === "win32" ? ".exe" : ""))
14841484

1485-
if (!(await Bun.file(bin).exists())) {
1485+
if (!(await Filesystem.exists(bin))) {
14861486
log.error("Failed to extract lua-language-server binary")
14871487
return
14881488
}
@@ -1516,7 +1516,7 @@ export namespace LSPServer {
15161516
const args: string[] = []
15171517
if (!binary) {
15181518
const js = path.join(Global.Path.bin, "node_modules", "intelephense", "lib", "intelephense.js")
1519-
if (!(await Bun.file(js).exists())) {
1519+
if (!(await Filesystem.exists(js))) {
15201520
if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return
15211521
await Bun.spawn([BunProc.which(), "install", "intelephense"], {
15221522
cwd: Global.Path.bin,
@@ -1613,7 +1613,7 @@ export namespace LSPServer {
16131613
const args: string[] = []
16141614
if (!binary) {
16151615
const js = path.join(Global.Path.bin, "node_modules", "bash-language-server", "out", "cli.js")
1616-
if (!(await Bun.file(js).exists())) {
1616+
if (!(await Filesystem.exists(js))) {
16171617
if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return
16181618
await Bun.spawn([BunProc.which(), "install", "bash-language-server"], {
16191619
cwd: Global.Path.bin,
@@ -1694,7 +1694,7 @@ export namespace LSPServer {
16941694
}
16951695

16961696
const tempPath = path.join(Global.Path.bin, assetName)
1697-
await Bun.file(tempPath).write(downloadResponse)
1697+
if (downloadResponse.body) await Filesystem.writeStream(tempPath, downloadResponse.body)
16981698

16991699
const ok = await Archive.extractZip(tempPath, Global.Path.bin)
17001700
.then(() => true)
@@ -1707,7 +1707,7 @@ export namespace LSPServer {
17071707

17081708
bin = path.join(Global.Path.bin, "terraform-ls" + (platform === "win32" ? ".exe" : ""))
17091709

1710-
if (!(await Bun.file(bin).exists())) {
1710+
if (!(await Filesystem.exists(bin))) {
17111711
log.error("Failed to extract terraform-ls binary")
17121712
return
17131713
}
@@ -1784,7 +1784,7 @@ export namespace LSPServer {
17841784
}
17851785

17861786
const tempPath = path.join(Global.Path.bin, assetName)
1787-
await Bun.file(tempPath).write(downloadResponse)
1787+
if (downloadResponse.body) await Filesystem.writeStream(tempPath, downloadResponse.body)
17881788

17891789
if (ext === "zip") {
17901790
const ok = await Archive.extractZip(tempPath, Global.Path.bin)
@@ -1803,7 +1803,7 @@ export namespace LSPServer {
18031803

18041804
bin = path.join(Global.Path.bin, "texlab" + (platform === "win32" ? ".exe" : ""))
18051805

1806-
if (!(await Bun.file(bin).exists())) {
1806+
if (!(await Filesystem.exists(bin))) {
18071807
log.error("Failed to extract texlab binary")
18081808
return
18091809
}
@@ -1832,7 +1832,7 @@ export namespace LSPServer {
18321832
const args: string[] = []
18331833
if (!binary) {
18341834
const js = path.join(Global.Path.bin, "node_modules", "dockerfile-language-server-nodejs", "lib", "server.js")
1835-
if (!(await Bun.file(js).exists())) {
1835+
if (!(await Filesystem.exists(js))) {
18361836
if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return
18371837
await Bun.spawn([BunProc.which(), "install", "dockerfile-language-server-nodejs"], {
18381838
cwd: Global.Path.bin,
@@ -1990,7 +1990,7 @@ export namespace LSPServer {
19901990
}
19911991

19921992
const tempPath = path.join(Global.Path.bin, assetName)
1993-
await Bun.file(tempPath).write(downloadResponse)
1993+
if (downloadResponse.body) await Filesystem.writeStream(tempPath, downloadResponse.body)
19941994

19951995
if (ext === "zip") {
19961996
const ok = await Archive.extractZip(tempPath, Global.Path.bin)
@@ -2008,7 +2008,7 @@ export namespace LSPServer {
20082008

20092009
bin = path.join(Global.Path.bin, "tinymist" + (platform === "win32" ? ".exe" : ""))
20102010

2011-
if (!(await Bun.file(bin).exists())) {
2011+
if (!(await Filesystem.exists(bin))) {
20122012
log.error("Failed to extract tinymist binary")
20132013
return
20142014
}

packages/opencode/src/util/filesystem.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
import { mkdir, readFile, writeFile } from "fs/promises"
2-
import { existsSync, statSync } from "fs"
1+
import { chmod, mkdir, readFile, writeFile } from "fs/promises"
2+
import { createWriteStream, existsSync, statSync } from "fs"
33
import { lookup } from "mime-types"
44
import { realpathSync } from "fs"
55
import { dirname, join, relative } from "path"
6+
import { Readable } from "stream"
7+
import { pipeline } from "stream/promises"
68

79
export namespace Filesystem {
810
// Fast sync version for metadata checks
@@ -68,6 +70,25 @@ export namespace Filesystem {
6870
return write(p, JSON.stringify(data, null, 2), mode)
6971
}
7072

73+
export async function writeStream(
74+
p: string,
75+
stream: ReadableStream<Uint8Array> | Readable,
76+
mode?: number,
77+
): Promise<void> {
78+
const dir = dirname(p)
79+
if (!existsSync(dir)) {
80+
await mkdir(dir, { recursive: true })
81+
}
82+
83+
const nodeStream = stream instanceof ReadableStream ? Readable.fromWeb(stream as any) : stream
84+
const writeStream = createWriteStream(p)
85+
await pipeline(nodeStream, writeStream)
86+
87+
if (mode) {
88+
await chmod(p, mode)
89+
}
90+
}
91+
7192
export function mimeType(p: string): string {
7293
return lookup(p) || "application/octet-stream"
7394
}

0 commit comments

Comments
 (0)