2026-03-26 18:04:31 +09:00
|
|
|
import { existsSync, readFileSync } from "node:fs"
|
2026-03-26 19:54:05 +09:00
|
|
|
import { join } from "node:path"
|
2026-03-26 18:04:31 +09:00
|
|
|
|
|
|
|
|
import { parseJsoncSafe } from "./jsonc-parser"
|
|
|
|
|
import { getOpenCodeConfigPaths } from "./opencode-config-dir"
|
|
|
|
|
import { LEGACY_PLUGIN_NAME, PLUGIN_NAME } from "./plugin-identity"
|
|
|
|
|
|
|
|
|
|
interface OpenCodeConfig {
|
|
|
|
|
plugin?: string[]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface LegacyPluginCheckResult {
|
|
|
|
|
hasLegacyEntry: boolean
|
|
|
|
|
hasCanonicalEntry: boolean
|
|
|
|
|
legacyEntries: string[]
|
2026-03-27 15:40:04 +09:00
|
|
|
configPath: string | null
|
2026-03-26 18:04:31 +09:00
|
|
|
}
|
|
|
|
|
|
2026-03-31 17:06:46 -07:00
|
|
|
function getConfigPathFromDirectory(configDir: string): string | null {
|
|
|
|
|
const jsonPath = join(configDir, "opencode.json")
|
|
|
|
|
const jsoncPath = join(configDir, "opencode.jsonc")
|
|
|
|
|
|
|
|
|
|
if (existsSync(jsoncPath)) return jsoncPath
|
|
|
|
|
if (existsSync(jsonPath)) return jsonPath
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getOpenCodeConfigPathsToCheck(overrideConfigDir?: string, projectDir?: string): string[] {
|
2026-03-26 19:54:05 +09:00
|
|
|
if (overrideConfigDir) {
|
2026-03-31 17:06:46 -07:00
|
|
|
const overridePath = getConfigPathFromDirectory(overrideConfigDir)
|
|
|
|
|
return overridePath ? [overridePath] : []
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const configPaths: string[] = []
|
|
|
|
|
|
|
|
|
|
if (projectDir) {
|
|
|
|
|
const projectConfigPath = getConfigPathFromDirectory(join(projectDir, ".opencode"))
|
|
|
|
|
if (projectConfigPath) {
|
|
|
|
|
configPaths.push(projectConfigPath)
|
|
|
|
|
}
|
2026-03-26 19:54:05 +09:00
|
|
|
}
|
|
|
|
|
|
2026-03-26 18:04:31 +09:00
|
|
|
const { configJsonc, configJson } = getOpenCodeConfigPaths({ binary: "opencode", version: null })
|
|
|
|
|
|
2026-03-31 17:06:46 -07:00
|
|
|
if (existsSync(configJsonc)) configPaths.push(configJsonc)
|
|
|
|
|
else if (existsSync(configJson)) configPaths.push(configJson)
|
|
|
|
|
|
|
|
|
|
return configPaths
|
2026-03-26 18:04:31 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function isLegacyPluginEntry(entry: string): boolean {
|
|
|
|
|
return entry === LEGACY_PLUGIN_NAME || entry.startsWith(`${LEGACY_PLUGIN_NAME}@`)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function isCanonicalPluginEntry(entry: string): boolean {
|
|
|
|
|
return entry === PLUGIN_NAME || entry.startsWith(`${PLUGIN_NAME}@`)
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-31 17:06:46 -07:00
|
|
|
export function checkForLegacyPluginEntry(
|
|
|
|
|
overrideConfigDir?: string,
|
|
|
|
|
projectDir?: string,
|
|
|
|
|
): LegacyPluginCheckResult {
|
|
|
|
|
const configPaths = getOpenCodeConfigPathsToCheck(overrideConfigDir, projectDir)
|
|
|
|
|
if (configPaths.length === 0) {
|
2026-03-27 15:40:04 +09:00
|
|
|
return { hasLegacyEntry: false, hasCanonicalEntry: false, legacyEntries: [], configPath: null }
|
2026-03-26 18:04:31 +09:00
|
|
|
}
|
|
|
|
|
|
2026-03-31 17:06:46 -07:00
|
|
|
let hasCanonicalEntry = false
|
|
|
|
|
let detectedConfigPath: string | null = null
|
|
|
|
|
|
|
|
|
|
for (const configPath of configPaths) {
|
|
|
|
|
detectedConfigPath ??= configPath
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const content = readFileSync(configPath, "utf-8")
|
|
|
|
|
const parseResult = parseJsoncSafe<OpenCodeConfig>(content)
|
|
|
|
|
if (!parseResult.data) {
|
|
|
|
|
continue
|
|
|
|
|
}
|
2026-03-26 18:04:31 +09:00
|
|
|
|
2026-03-31 17:06:46 -07:00
|
|
|
const pluginEntries = parseResult.data.plugin ?? []
|
|
|
|
|
const legacyEntries = pluginEntries.filter(isLegacyPluginEntry)
|
|
|
|
|
const fileHasCanonicalEntry = pluginEntries.some(isCanonicalPluginEntry)
|
2026-03-26 18:04:31 +09:00
|
|
|
|
2026-03-31 17:06:46 -07:00
|
|
|
if (legacyEntries.length > 0) {
|
|
|
|
|
return {
|
|
|
|
|
hasLegacyEntry: true,
|
|
|
|
|
hasCanonicalEntry: fileHasCanonicalEntry,
|
|
|
|
|
legacyEntries,
|
|
|
|
|
configPath,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hasCanonicalEntry ||= fileHasCanonicalEntry
|
|
|
|
|
} catch {
|
|
|
|
|
continue
|
2026-03-26 18:04:31 +09:00
|
|
|
}
|
2026-03-31 17:06:46 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
hasLegacyEntry: false,
|
|
|
|
|
hasCanonicalEntry,
|
|
|
|
|
legacyEntries: [],
|
|
|
|
|
configPath: detectedConfigPath,
|
2026-03-26 18:04:31 +09:00
|
|
|
}
|
|
|
|
|
}
|