Explorar o código

fix(lsp): MEMORY LEAK: ensure typescript server uses native project config (#19953)

Derek Barrera hai 5 meses
pai
achega
01f0319192

+ 11 - 1
packages/opencode/src/lsp/server.ts

@@ -105,7 +105,17 @@ export namespace LSPServer {
       if (!tsserver) return
       const bin = await Npm.which("typescript-language-server")
       if (!bin) return
-      const proc = spawn(bin, ["--stdio"], {
+
+      const args = ["--stdio", "--tsserver-log-verbosity", "off", "--tsserver-path", tsserver]
+
+      if (
+        !(await pathExists(path.join(root, "tsconfig.json"))) &&
+        !(await pathExists(path.join(root, "jsconfig.json")))
+      ) {
+        args.push("--ignore-node-modules")
+      }
+
+      const proc = spawn(bin, args, {
         cwd: root,
         env: {
           ...process.env,

+ 78 - 0
packages/opencode/test/lsp/index.test.ts

@@ -1,6 +1,8 @@
 import { describe, expect, spyOn, test } from "bun:test"
 import path from "path"
+import fs from "fs/promises"
 import * as Lsp from "../../src/lsp/index"
+import * as launch from "../../src/lsp/launch"
 import { LSPServer } from "../../src/lsp/server"
 import { Instance } from "../../src/project/instance"
 import { tmpdir } from "../fixture/fixture"
@@ -52,4 +54,80 @@ describe("lsp.spawn", () => {
       await Instance.disposeAll()
     }
   })
+
+  test("spawns builtin Typescript LSP with correct arguments", async () => {
+    await using tmp = await tmpdir()
+    
+    // Create dummy tsserver to satisfy Module.resolve
+    const tsdk = path.join(tmp.path, "node_modules", "typescript", "lib")
+    await fs.mkdir(tsdk, { recursive: true })
+    await fs.writeFile(path.join(tsdk, "tsserver.js"), "")
+
+    const spawnSpy = spyOn(launch, "spawn").mockImplementation(
+      () =>
+        ({
+          stdin: {},
+          stdout: {},
+          stderr: {},
+          on: () => {},
+          kill: () => {},
+        }) as any,
+    )
+
+    try {
+      await Instance.provide({
+        directory: tmp.path,
+        fn: async () => {
+          await LSPServer.Typescript.spawn(tmp.path)
+        },
+      })
+
+      expect(spawnSpy).toHaveBeenCalled()
+      const args = spawnSpy.mock.calls[0][1] as string[]
+
+      expect(args).toContain("--tsserver-path")
+      expect(args).toContain("--tsserver-log-verbosity")
+      expect(args).toContain("off")
+    } finally {
+      spawnSpy.mockRestore()
+    }
+  })
+
+  test("spawns builtin Typescript LSP with --ignore-node-modules if no config is found", async () => {
+    await using tmp = await tmpdir()
+
+    // Create dummy tsserver to satisfy Module.resolve
+    const tsdk = path.join(tmp.path, "node_modules", "typescript", "lib")
+    await fs.mkdir(tsdk, { recursive: true })
+    await fs.writeFile(path.join(tsdk, "tsserver.js"), "")
+
+    // NO tsconfig.json or jsconfig.json created here
+
+    const spawnSpy = spyOn(launch, "spawn").mockImplementation(
+      () =>
+        ({
+          stdin: {},
+          stdout: {},
+          stderr: {},
+          on: () => {},
+          kill: () => {},
+        }) as any,
+    )
+
+    try {
+      await Instance.provide({
+        directory: tmp.path,
+        fn: async () => {
+          await LSPServer.Typescript.spawn(tmp.path)
+        },
+      })
+
+      expect(spawnSpy).toHaveBeenCalled()
+      const args = spawnSpy.mock.calls[0][1] as string[]
+
+      expect(args).toContain("--ignore-node-modules")
+    } finally {
+      spawnSpy.mockRestore()
+    }
+  })
 })

+ 2 - 0
packages/web/src/content/docs/ecosystem.mdx

@@ -32,6 +32,7 @@ You can also check out [awesome-opencode](https://github.com/awesome-opencode/aw
 | [opencode-shell-strategy](https://github.com/JRedeker/opencode-shell-strategy)                     | Instructions for non-interactive shell commands - prevents hangs from TTY-dependent operations    |
 | [opencode-wakatime](https://github.com/angristan/opencode-wakatime)                                | Track OpenCode usage with Wakatime                                                                |
 | [opencode-md-table-formatter](https://github.com/franlol/opencode-md-table-formatter/tree/main)    | Clean up markdown tables produced by LLMs                                                         |
+| [opencode-morph-fast-apply](https://github.com/JRedeker/opencode-morph-fast-apply)                 | 10x faster code editing with Morph Fast Apply API and lazy edit markers                           |
 | [opencode-morph-plugin](https://github.com/morphllm/opencode-morph-plugin)                         | Fast Apply editing, WarpGrep codebase search, and context compaction via Morph                    |
 | [oh-my-opencode](https://github.com/code-yeongyu/oh-my-opencode)                                   | Background agents, pre-built LSP/AST/MCP tools, curated agents, Claude Code compatible            |
 | [opencode-notificator](https://github.com/panta82/opencode-notificator)                            | Desktop notifications and sound alerts for OpenCode sessions                                      |
@@ -42,6 +43,7 @@ You can also check out [awesome-opencode](https://github.com/awesome-opencode/aw
 | [@plannotator/opencode](https://github.com/backnotprop/plannotator/tree/main/apps/opencode-plugin) | Interactive plan review with visual annotation and private/offline sharing                        |
 | [@openspoon/subtask2](https://github.com/spoons-and-mirrors/subtask2)                              | Extend opencode /commands into a powerful orchestration system with granular flow control         |
 | [opencode-scheduler](https://github.com/different-ai/opencode-scheduler)                           | Schedule recurring jobs using launchd (Mac) or systemd (Linux) with cron syntax                   |
+| [opencode-conductor](https://github.com/derekbar90/opencode-conductor)                             | Protocol-Driven Workflow: Automation of the Context -> Spec -> Plan -> Implement lifecycle.       |
 | [micode](https://github.com/vtemian/micode)                                                        | Structured Brainstorm → Plan → Implement workflow with session continuity                         |
 | [octto](https://github.com/vtemian/octto)                                                          | Interactive browser UI for AI brainstorming with multi-question forms                             |
 | [opencode-background-agents](https://github.com/kdcokenny/opencode-background-agents)              | Claude Code-style background agents with async delegation and context persistence                 |