fix(config): load lsp config from jsonc configuration files

Signed-off-by: Raphanus Lo <coldturnip@gmail.com>
This commit is contained in:
Raphanus Lo
2026-02-11 17:40:06 +08:00
parent 3b2d3acd17
commit f80b72c2b7
4 changed files with 141 additions and 18 deletions
+125 -3
View File
@@ -1,8 +1,8 @@
import { describe, it, expect } from "bun:test"
import { writeFileSync, unlinkSync } from "fs"
import { writeFileSync, unlinkSync, mkdirSync, rmSync } from "fs"
import { join } from "path"
import { tmpdir } from "os"
import { loadJsonFile } from "./server-config-loader"
import { loadJsonFile, getConfigPaths, getMergedServers } from "./server-config-loader"
describe("loadJsonFile", () => {
it("parses JSONC config files with comments correctly", () => {
@@ -36,4 +36,126 @@ describe("loadJsonFile", () => {
// cleanup
unlinkSync(tempPath)
})
})
it("discovers JSONC-only user config (oh-my-opencode.jsonc)", () => {
const originalEnv = process.env.OPENCODE_CONFIG_DIR
const tempBase = join(tmpdir(), `omo-test-user-jsonc-${Date.now()}-${Math.random().toString(36).slice(2)}`)
try {
mkdirSync(tempBase, { recursive: true })
process.env.OPENCODE_CONFIG_DIR = tempBase
const userJsonc = `{
// user jsonc config
"lsp": {
"user-jsonc": {
"command": ["user-jsonc-cmd"],
"extensions": [".ujs"]
}
}
}`
const userPath = join(tempBase, "oh-my-opencode.jsonc")
writeFileSync(userPath, userJsonc, "utf-8")
const servers = getMergedServers()
const found = servers.find(s => s.id === "user-jsonc" && s.source === "user")
expect(found !== undefined).toBe(true)
} finally {
if (originalEnv === undefined) delete process.env.OPENCODE_CONFIG_DIR
else process.env.OPENCODE_CONFIG_DIR = originalEnv
rmSync(tempBase, { recursive: true, force: true })
}
})
it("discovers JSONC-only opencode config (opencode.jsonc)", () => {
const originalEnv = process.env.OPENCODE_CONFIG_DIR
const tempBase = join(tmpdir(), `omo-test-oc-jsonc-${Date.now()}-${Math.random().toString(36).slice(2)}`)
try {
mkdirSync(tempBase, { recursive: true })
process.env.OPENCODE_CONFIG_DIR = tempBase
const opencodeJsonc = `{
// opencode jsonc config
"lsp": {
"opencode-jsonc": {
"command": ["opencode-jsonc-cmd"],
"extensions": [".ocjs"]
}
}
}`
const opencodePath = join(tempBase, "opencode.jsonc")
writeFileSync(opencodePath, opencodeJsonc, "utf-8")
const servers = getMergedServers()
const found = servers.find(s => s.id === "opencode-jsonc" && s.source === "opencode")
expect(found !== undefined).toBe(true)
} finally {
if (originalEnv === undefined) delete process.env.OPENCODE_CONFIG_DIR
else process.env.OPENCODE_CONFIG_DIR = originalEnv
rmSync(tempBase, { recursive: true, force: true })
}
})
it("discovers JSONC-only project config (.opencode/oh-my-opencode.jsonc)", () => {
const originalCwd = process.cwd()
const tempProject = join(tmpdir(), `omo-test-project-jsonc-${Date.now()}-${Math.random().toString(36).slice(2)}`)
try {
mkdirSync(join(tempProject, ".opencode"), { recursive: true })
const projectJsonc = `{
// project jsonc config
"lsp": {
"project-jsonc": {
"command": ["project-jsonc-cmd"],
"extensions": [".pjs"]
}
}
}`
const projectPath = join(tempProject, ".opencode", "oh-my-opencode.jsonc")
writeFileSync(projectPath, projectJsonc, "utf-8")
process.chdir(tempProject)
const servers = getMergedServers()
const found = servers.find(s => s.id === "project-jsonc" && s.source === "project")
expect(found !== undefined).toBe(true)
} finally {
process.chdir(originalCwd)
rmSync(tempProject, { recursive: true, force: true })
}
})
it("prefers .jsonc over .json when both exist for same config id", () => {
const originalEnv = process.env.OPENCODE_CONFIG_DIR
const tempBase = join(tmpdir(), `omo-test-precedence-${Date.now()}-${Math.random().toString(36).slice(2)}`)
try {
mkdirSync(tempBase, { recursive: true })
process.env.OPENCODE_CONFIG_DIR = tempBase
const jsonContent = `{
"lsp": {
"conflict": {
"command": ["from-json"],
"extensions": [".j"]
}
}
}`
const jsoncContent = `{
// jsonc should take precedence
"lsp": {
"conflict": {
"command": ["from-jsonc"],
"extensions": [".jc"]
}
}
}`
writeFileSync(join(tempBase, "oh-my-opencode.json"), jsonContent, "utf-8")
writeFileSync(join(tempBase, "oh-my-opencode.jsonc"), jsoncContent, "utf-8")
const servers = getMergedServers()
const found = servers.find(s => s.id === "conflict" && s.source === "user")
expect(found?.command && Array.isArray(found.command) && found.command[0] === "from-jsonc").toBe(true)
} finally {
if (originalEnv === undefined) delete process.env.OPENCODE_CONFIG_DIR
else process.env.OPENCODE_CONFIG_DIR = originalEnv
rmSync(tempBase, { recursive: true, force: true })
}
})
})
+4 -4
View File
@@ -4,7 +4,7 @@ import { join } from "path"
import { BUILTIN_SERVERS } from "./constants"
import type { ResolvedServer } from "./types"
import { getOpenCodeConfigDir } from "../../shared"
import { parseJsonc } from "../../shared/jsonc-parser"
import { parseJsonc, detectConfigFile } from "../../shared/jsonc-parser"
interface LspEntry {
disabled?: boolean
@@ -38,9 +38,9 @@ export function getConfigPaths(): { project: string; user: string; opencode: str
const cwd = process.cwd()
const configDir = getOpenCodeConfigDir({ binary: "opencode" })
return {
project: join(cwd, ".opencode", "oh-my-opencode.json"),
user: join(configDir, "oh-my-opencode.json"),
opencode: join(configDir, "opencode.json"),
project: detectConfigFile(join(cwd, ".opencode", "oh-my-opencode")).path,
user: detectConfigFile(join(configDir, "oh-my-opencode")).path,
opencode: detectConfigFile(join(configDir, "opencode")).path,
}
}