cloudaxe-opencode/packages/opencode/src/format/index.ts

98 lines
2.6 KiB
TypeScript
Raw Normal View History

2025-06-27 02:30:44 +00:00
import { App } from "../app/app"
2025-06-27 15:29:20 +00:00
import { Bus } from "../bus"
import { File } from "../file"
2025-06-27 02:30:44 +00:00
import { Log } from "../util/log"
import path from "path"
2025-06-30 01:30:23 +00:00
import * as Formatter from "./formatter"
2025-07-31 22:56:04 +00:00
import { Config } from "../config/config"
2025-07-31 23:35:55 +00:00
import { mergeDeep } from "remeda"
2025-06-30 01:13:32 +00:00
export namespace Format {
2025-06-27 02:30:44 +00:00
const log = Log.create({ service: "format" })
2025-07-31 23:35:55 +00:00
const state = App.state("format", async () => {
2025-06-27 15:29:20 +00:00
const enabled: Record<string, boolean> = {}
2025-07-31 23:35:55 +00:00
const cfg = await Config.get()
const formatters = { ...Formatter } as Record<string, Formatter.Info>
for (const [name, item] of Object.entries(cfg.formatter ?? {})) {
if (item.disabled) {
delete formatters[name]
continue
}
const result: Formatter.Info = mergeDeep(formatters[name] ?? {}, {
command: [],
extensions: [],
...item,
})
result.enabled = async () => true
result.name = name
formatters[name] = result
}
return {
2025-06-27 15:29:20 +00:00
enabled,
2025-07-31 23:35:55 +00:00
formatters,
}
})
2025-06-30 01:30:23 +00:00
async function isEnabled(item: Formatter.Info) {
2025-07-31 23:35:55 +00:00
const s = await state()
2025-06-27 15:29:20 +00:00
let status = s.enabled[item.name]
if (status === undefined) {
status = await item.enabled()
s.enabled[item.name] = status
}
return status
}
2025-06-27 15:29:20 +00:00
async function getFormatter(ext: string) {
2025-07-31 23:35:55 +00:00
const formatters = await state().then((x) => x.formatters)
2025-06-27 15:29:20 +00:00
const result = []
2025-07-31 23:35:55 +00:00
for (const item of Object.values(formatters)) {
log.info("checking", { name: item.name, ext })
2025-06-27 15:29:20 +00:00
if (!item.extensions.includes(ext)) continue
if (!(await isEnabled(item))) continue
2025-06-27 15:29:20 +00:00
result.push(item)
}
2025-06-27 15:29:20 +00:00
return result
}
2025-06-27 15:29:20 +00:00
export function init() {
log.info("init")
Bus.subscribe(File.Event.Edited, async (payload) => {
const file = payload.properties.file
log.info("formatting", { file })
const ext = path.extname(file)
for (const item of await getFormatter(ext)) {
log.info("running", { command: item.command })
2025-08-22 21:09:15 +00:00
try {
const proc = Bun.spawn({
cmd: item.command.map((x) => x.replace("$FILE", file)),
cwd: App.info().path.cwd,
env: { ...process.env, ...item.environment },
stdout: "ignore",
stderr: "ignore",
})
const exit = await proc.exited
if (exit !== 0)
log.error("failed", {
command: item.command,
...item.environment,
})
} catch (error) {
2025-06-27 15:29:20 +00:00
log.error("failed", {
2025-08-22 21:09:15 +00:00
error,
2025-06-27 15:29:20 +00:00
command: item.command,
...item.environment,
})
2025-08-22 21:09:15 +00:00
// re-raising
throw error
}
2025-06-27 15:29:20 +00:00
}
})
}
}