#!/usr/bin/env node /** Ensure app/accounting routes remain thin adapters. */ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs"; import { join, relative, sep } from "node:path"; import { fileURLToPath } from "node:url"; const ROOT = join(fileURLToPath(new URL(".", import.meta.url)), ".."); const ACC = join(ROOT, "app", "accounting"); /** @type {string[]} */ const issues = []; function walkPages(dir, pages = []) { for (const entry of readdirSync(dir)) { const full = join(dir, entry); if (statSync(full).isDirectory()) walkPages(full, pages); else if (entry === "page.tsx") pages.push(full); } return pages; } if (!existsSync(ACC)) { console.error("āŒ app/accounting missing"); process.exit(1); } if (!existsSync(join(ACC, "layout.tsx"))) { issues.push("Missing layout: app/accounting/layout.tsx"); } const layout = readFileSync(join(ACC, "layout.tsx"), "utf8"); if (!layout.includes("@/src/modules/accounting")) { issues.push("layout.tsx must import from @/src/modules/accounting"); } const pages = walkPages(ACC); if (pages.length < 100) issues.push(`Expected >= 100 pages, found ${pages.length}`); for (const page of pages) { const rel = relative(ROOT, page).split(sep).join("/"); const content = readFileSync(page, "utf8"); if (!/export\s+(default|\{)/.test(content)) { issues.push(`${rel}: missing default export`); } const lines = content.split("\n").length; if (lines > 8 && !content.includes("@/src/modules/accounting")) { issues.push(`${rel}: route file should re-export from src/modules/accounting (${lines} lines)`); } if (content.includes("@/lib/accounting-api") || content.includes("@/components/accounting")) { issues.push(`${rel}: route must not import legacy accounting paths`); } } console.log("Accounting Route Validation\n"); console.log(`Pages: ${pages.length}`); if (issues.length) { console.error(`\nāŒ ${issues.length} issue(s):`); for (const i of issues) console.error(` - ${i}`); process.exit(1); } console.log("\nāœ… Routes and layout intact"); process.exit(0);