import { dirname, relative, join } from "node:path" import { existsSync } from "node:fs" import { type RegimeConfig, findRegimeConfigs, resolveTemplateChain, getStrategy, interpolate, readFileSync, diffJson, mergeTemplateJsonFiles, mergeTemplateJsoncFiles, parseJsonc, } from "./shared" const red = Bun.color("red", "ansi") const orange = Bun.color("orange", "ansi") const green = Bun.color("green", "ansi") const purple = Bun.color("purple", "ansi") const reset = "\x1b[0m" export async function check(targetDir: string, full = false): Promise { const rcFiles = await findRegimeConfigs(targetDir) if (rcFiles.length === 0) { console.log("No regime.config.json files found.") return } for (const rcFile of rcFiles) { const rcDir = dirname(rcFile) const relDir = relative(targetDir, rcDir) || "." console.log(`\n${purple}${relDir}/${reset}`) const rc: RegimeConfig = JSON.parse(readFileSync(rcFile)) const templateNames = Array.isArray(rc.templates) ? rc.templates : [rc.templates] const vars = rc.vars ?? {} const { files, patterns } = resolveTemplateChain(templateNames) if (files.size === 0) { console.log(" (no template files)") continue } let synced = true for (const [relPath, templatePaths] of files) { const targetRelPath = interpolate(relPath, vars) const targetPath = join(rcDir, targetRelPath) const strategy = getStrategy(targetRelPath, patterns) if (!existsSync(targetPath)) { console.log(` ${targetRelPath}: ${red}missing${reset}`) synced = false continue } const existingContent = readFileSync(targetPath) if (strategy === "merge json") { try { const templateObj = mergeTemplateJsonFiles(templatePaths, vars, targetRelPath) const existingObj = JSON.parse(existingContent) const entries = diffJson(templateObj, existingObj, full) const diffs = entries.filter(e => !e.ok) if (diffs.length > 0 || (full && entries.length > 0)) { if (diffs.length > 0) synced = false console.log(` ${targetRelPath}:`) for (const d of entries) { if (d.ok) { console.log(` ${d.field}: ${green}ok${reset}`) } else { const exp = JSON.stringify(d.expected) const act = d.actual === undefined ? `${red}missing${reset}` : `${orange}${JSON.stringify(d.actual)}${reset}` console.log(` ${d.field}: ${act} -> ${green}${exp}${reset}`) } } } else if (full) { console.log(` ${targetRelPath}: ${green}ok${reset}`) } } catch (e) { console.log(` ${targetRelPath}: ${red}failed to parse JSON${reset} - ${e}`) synced = false } } else if (strategy === "merge jsonc") { try { const templateObj = mergeTemplateJsoncFiles(templatePaths, vars, targetRelPath) const existingObj = parseJsonc(existingContent) const entries = diffJson(templateObj, existingObj, full) const diffs = entries.filter(e => !e.ok) if (diffs.length > 0 || (full && entries.length > 0)) { if (diffs.length > 0) synced = false console.log(` ${targetRelPath}:`) for (const d of entries) { if (d.ok) { console.log(` ${d.field}: ${green}ok${reset}`) } else { const exp = JSON.stringify(d.expected) const act = d.actual === undefined ? `${red}missing${reset}` : `${orange}${JSON.stringify(d.actual)}${reset}` console.log(` ${d.field}: ${act} -> ${green}${exp}${reset}`) } } } else if (full) { console.log(` ${targetRelPath}: ${green}ok${reset}`) } } catch (e) { console.log(` ${targetRelPath}: ${red}failed to parse JSONC${reset} - ${e}`) synced = false } } else if (strategy === "overwrite") { const templateContent = interpolate(readFileSync(templatePaths[templatePaths.length - 1]), vars, targetRelPath) if (existingContent !== templateContent) { console.log(` ${targetRelPath}: ${orange}differs${reset}`) synced = false } else if (full) { console.log(` ${targetRelPath}: ${green}ok${reset}`) } } } if (synced) { console.log(` ${green}in sync${reset}`) } } }