import { dirname, relative, join } from "node:path" import { existsSync } from "node:fs" import { writeFile } from "node:fs/promises" import { type RegimeConfig, findRegimeConfigs, resolveTemplateChain, getStrategy, interpolate, readFileSync, deepMerge, mergeTemplateJsonFiles, detectIndent, } from "./shared" const green = Bun.color("green", "ansi") const yellow = Bun.color("orange", "ansi") const red = Bun.color("red", "ansi") const purple = Bun.color("purple", "ansi") const reset = "\x1b[0m" export async function sync(targetDir: string): 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 allSynced = true for (const [relPath, templatePaths] of files) { const targetPath = join(rcDir, relPath) const strategy = getStrategy(relPath, patterns) // Check target directory exists const targetFileDir = dirname(targetPath) if (!existsSync(targetFileDir)) { console.log(` ${relPath}: ${yellow}warning: directory does not exist, skipping${reset}`) continue } if (strategy === "merge json") { let templateObj: any try { templateObj = mergeTemplateJsonFiles(templatePaths, vars, relPath) } catch (e) { console.log(` ${relPath}: ${red}failed to parse template JSON${reset} - ${e}`) continue } if (existsSync(targetPath)) { const existingContent = readFileSync(targetPath) let existingObj: any try { existingObj = JSON.parse(existingContent) } catch (e) { console.log(` ${relPath}: ${red}failed to parse existing JSON${reset} - ${e}`) continue } const merged = deepMerge(existingObj, templateObj) const indent = detectIndent(existingContent) const mergedContent = JSON.stringify(merged, null, indent) + "\n" if (mergedContent === existingContent) { // already in sync continue } await writeFile(targetPath, mergedContent) console.log(` ${relPath}: ${green}updated${reset}`) allSynced = false } else { const content = JSON.stringify(templateObj, null, " ") + "\n" await writeFile(targetPath, content) console.log(` ${relPath}: ${green}created${reset}`) allSynced = false } } else if (strategy === "overwrite") { const templateContent = interpolate( readFileSync(templatePaths[templatePaths.length - 1]), vars, relPath, ) if (existsSync(targetPath)) { const existingContent = readFileSync(targetPath) if (existingContent === templateContent) { // already in sync continue } await writeFile(targetPath, templateContent) console.log(` ${relPath}: ${green}updated${reset}`) allSynced = false } else { await writeFile(targetPath, templateContent) console.log(` ${relPath}: ${green}created${reset}`) allSynced = false } } } if (allSynced) { console.log(` ${green}in sync${reset}`) } } }