import { walk } from "jsr:@std/fs"; const TARGET_DIR = "./src"; const MAX_FILE_LINES = 400; // Auditable allowlist for exceptional files that legitimately exceed MAX_FILE_LINES. // Every entry must be explicitly documented and approved. const ALLOWLISTED_LARGE_FILES = new Set([ // Currently empty: all modules in src/ must strictly conform to <= 400 lines. ]); let hasErrors = false; async function checkFile(path: string) { const content = await Deno.readTextFile(path); const lines = content.split("\n"); // 1. Enforce strict 400-line ceiling if (lines.length > MAX_FILE_LINES && !ALLOWLISTED_LARGE_FILES.has(path)) { console.error( `[Arch Lint] ❌ File size ceiling exceeded in ${path}: ${lines.length} lines (Hard Ceiling: ${MAX_FILE_LINES} lines).`, ); console.error( ` -> Subdivide this module into focused, SRP-aligned component files within the feature directory (e.g. login_fragments.tsx, register_fragments.tsx).`, ); hasErrors = true; } lines.forEach((line, index) => { // 2. Block anti-formatting minification hacks (lines over 300 chars without SVG/CSS/raw string reasons) if ( line.length > 300 && !line.includes(" Format code with standard 'deno fmt'. Do not minify to bypass line limits.`, ); hasErrors = true; } // 3. Block banned DOM APIs in src/ if ( line.includes("document.getElementById") || line.includes("document.querySelector") || line.includes("document.createElement") ) { console.error( `[Arch Lint] ❌ Banned DOM API used in ${path}:${index + 1}`, ); console.error(` ${line.trim()}`); console.error( ` -> Use Datastar reactive attributes or SSE morphs instead.`, ); hasErrors = true; } // 4. Block unescaped HTML in raw strings (basic heuristic for dangerouslySetInnerHTML) if ( line.includes("dangerouslySetInnerHTML") && !path.includes("error_fragments.tsx") ) { console.error( `[Arch Lint] ❌ dangerouslySetInnerHTML used in ${path}:${index + 1}`, ); console.error(` ${line.trim()}`); console.error( ` -> Native JSX HTML escaping should be used unless in explicit core fragments.`, ); hasErrors = true; } }); } for await (const entry of walk(TARGET_DIR, { exts: [".ts", ".tsx"] })) { if (entry.isFile) { await checkFile(entry.path); } } if (hasErrors) { Deno.exit(1); } else { console.log("✅ Architecture lint passed (All files <= 400 lines & clean)."); }