import { copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; import { basename, join, resolve } from "node:path"; import { CURRENT_COMPATIBILITY_DATE, CURRENT_FRAMEWORK_BEHAVIOUR, loadRawConfig, resolveCompatibility, } from "@wrnexus/styles"; const CONFIG_NAMES = ["wrnexus.config.ts", "wrnexus.config.js", "wrnexus.config.mjs"]; function configPath(root: string): string | undefined { return CONFIG_NAMES.map((name) => join(root, name)).find(existsSync); } export async function compatibilityReport(appRoot: string) { return resolveCompatibility(await loadRawConfig(resolve(appRoot))); } export function upgradeCompatibility(appRoot: string): { file: string; backup: string; changed: boolean; } { const root = resolve(appRoot); const file = configPath(root); if (!file) throw new Error("WRN-COMPATIBILITY-NO-CONFIG: wrnexus.config.ts was not found."); const source = readFileSync(file, "utf8"); let updated = source; const replace = (name: string, value: string) => { const pattern = new RegExp(`(^\\s*${name}\\s*:\\s*)(?:["'][^"']*["']|\\d+)(\\s*,?)`, "m"); if (pattern.test(updated)) updated = updated.replace(pattern, `$1${value}$2`); else { const object = /(?:const\s+config[^=]*=|defineConfig\s*\(|export\s+default)\s*\{/m; if (!object.test(updated)) throw new Error("WRN-COMPATIBILITY-CONFIG-SHAPE: unable to locate the root config object."); updated = updated.replace(object, (match) => `${match}\n ${name}: ${value},`); } }; replace("compatibilityDate", JSON.stringify(CURRENT_COMPATIBILITY_DATE)); replace("frameworkBehaviour", String(CURRENT_FRAMEWORK_BEHAVIOUR)); if (updated === source) return { file, backup: "", changed: false }; const directory = join(root, ".wrnexus", "compatibility-backups"); mkdirSync(directory, { recursive: true }); const backup = join(directory, `${Date.now()}-${basename(file)}`); copyFileSync(file, backup); writeFileSync(file, updated, "utf8"); return { file, backup, changed: true }; } export async function runCompatibilityCommand( appRoot: string, command = "check", args: string[] = [], ): Promise { if (command === "upgrade") { const result = upgradeCompatibility(appRoot); if (args.includes("--json")) console.log(JSON.stringify(result, null, 2)); else console.log( result.changed ? `✓ Compatibility policy upgraded\n backup: ${result.backup}` : "✓ Compatibility policy already current", ); } const report = await compatibilityReport(appRoot); if (args.includes("--json")) console.log(JSON.stringify(report, null, 2)); else { console.log(`Compatibility date: ${report.effectiveDate} (current ${report.currentDate})`); console.log( `Framework behaviour: ${report.effectiveBehaviour} (current ${report.currentBehaviour})`, ); for (const message of report.messages) console.log(`- ${message}`); } return !report.needsUpgrade && !report.future; }