77 lines
3.0 KiB
TypeScript
77 lines
3.0 KiB
TypeScript
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<boolean> {
|
|
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;
|
|
}
|