2026-04-01 18:33:29 -07:00
|
|
|
import { closeSync, existsSync, fsyncSync, openSync, readFileSync, renameSync, writeFileSync } from "node:fs"
|
2026-04-04 02:10:27 +09:00
|
|
|
|
2026-03-28 15:24:18 +09:00
|
|
|
import { applyEdits, modify } from "jsonc-parser"
|
2026-03-27 15:40:04 +09:00
|
|
|
|
2026-03-28 15:24:18 +09:00
|
|
|
import { parseJsoncSafe } from "./jsonc-parser"
|
2026-03-27 15:40:04 +09:00
|
|
|
import { log } from "./logger"
|
|
|
|
|
import { LEGACY_PLUGIN_NAME, PLUGIN_NAME } from "./plugin-identity"
|
2026-04-04 02:10:27 +09:00
|
|
|
import { isCanonicalEntry, isLegacyEntry, toCanonicalEntry } from "./plugin-entry-migrator"
|
2026-03-27 15:40:04 +09:00
|
|
|
|
2026-03-28 15:24:18 +09:00
|
|
|
interface OpenCodeConfig {
|
|
|
|
|
plugin?: string[]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function normalizePluginEntries(entries: string[]): string[] {
|
|
|
|
|
const hasCanonical = entries.some(isCanonicalEntry)
|
|
|
|
|
|
|
|
|
|
if (hasCanonical) {
|
|
|
|
|
return entries.filter((entry) => !isLegacyEntry(entry))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return entries.map((entry) => (isLegacyEntry(entry) ? toCanonicalEntry(entry) : entry))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function updateJsoncPluginArray(content: string, pluginEntries: string[]): string | null {
|
|
|
|
|
const edits = modify(content, ["plugin"], pluginEntries, {
|
|
|
|
|
formattingOptions: {
|
|
|
|
|
insertSpaces: true,
|
|
|
|
|
tabSize: 2,
|
|
|
|
|
eol: "\n",
|
|
|
|
|
},
|
|
|
|
|
getInsertionIndex: () => 0,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if (edits.length === 0) return null
|
|
|
|
|
return applyEdits(content, edits)
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-27 15:40:04 +09:00
|
|
|
export function migrateLegacyPluginEntry(configPath: string): boolean {
|
|
|
|
|
if (!existsSync(configPath)) return false
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const content = readFileSync(configPath, "utf-8")
|
|
|
|
|
if (!content.includes(LEGACY_PLUGIN_NAME)) return false
|
|
|
|
|
|
2026-03-28 15:24:18 +09:00
|
|
|
const parseResult = parseJsoncSafe<OpenCodeConfig>(content)
|
|
|
|
|
const pluginEntries = parseResult.data?.plugin
|
|
|
|
|
if (!pluginEntries || !pluginEntries.some(isLegacyEntry)) return false
|
|
|
|
|
|
|
|
|
|
const updatedPluginEntries = normalizePluginEntries(pluginEntries)
|
|
|
|
|
const updated = configPath.endsWith(".jsonc")
|
|
|
|
|
? updateJsoncPluginArray(content, updatedPluginEntries)
|
|
|
|
|
: JSON.stringify({ ...(parseResult.data as OpenCodeConfig), plugin: updatedPluginEntries }, null, 2) + "\n"
|
|
|
|
|
if (!updated || updated === content) return false
|
2026-03-27 15:40:04 +09:00
|
|
|
|
2026-04-01 18:33:29 -07:00
|
|
|
const tempPath = `${configPath}.tmp`
|
|
|
|
|
writeFileSync(tempPath, updated, "utf-8")
|
|
|
|
|
const tempFileDescriptor = openSync(tempPath, "r")
|
|
|
|
|
try {
|
|
|
|
|
fsyncSync(tempFileDescriptor)
|
|
|
|
|
} finally {
|
|
|
|
|
closeSync(tempFileDescriptor)
|
|
|
|
|
}
|
2026-04-04 02:10:27 +09:00
|
|
|
|
2026-04-01 18:33:29 -07:00
|
|
|
renameSync(tempPath, configPath)
|
2026-03-27 15:40:04 +09:00
|
|
|
log("[migrateLegacyPluginEntry] Auto-migrated opencode.json plugin entry", {
|
|
|
|
|
configPath,
|
|
|
|
|
from: LEGACY_PLUGIN_NAME,
|
|
|
|
|
to: PLUGIN_NAME,
|
|
|
|
|
})
|
|
|
|
|
return true
|
|
|
|
|
} catch (error) {
|
|
|
|
|
log("[migrateLegacyPluginEntry] Failed to migrate opencode.json", { configPath, error })
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
}
|