#!/usr/bin/env node /** Validate Healthcare module import boundaries. */ import { 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 EXT = new Set([".ts", ".tsx"]); const importRegex = /from\s+["']([^"']+)["']/g; const LEGACY = [ "@/components/healthcare/", "@/lib/healthcare-api", "@/lib/healthcare-portals", "@/hooks/useHealthcareLookups", "@/hooks/useHealthcarePatientContext", "@/hooks/useHealthcareDoctorContext", ]; const FORBIDDEN = [ "@/components/accounting/", "@/components/beauty/", "@/modules/beauty/", "@/modules/accounting/", "@/src/modules/beauty/", ]; function walk(dir, files = []) { for (const entry of readdirSync(dir)) { if (entry === "node_modules" || entry === ".next") continue; const full = join(dir, entry); if (statSync(full).isDirectory()) walk(full, files); else if (EXT.has(entry.slice(entry.lastIndexOf(".")))) files.push(full); } return files; } /** @type {Array<{file: string, importPath: string, reason: string}>} */ const violations = []; for (const scope of ["app/healthcare", "src/modules/healthcare"]) { const abs = join(ROOT, scope); if (!statSync(abs, { throwIfNotFound: false })?.isDirectory()) continue; for (const file of walk(abs)) { const rel = relative(ROOT, file).split(sep).join("/"); const content = readFileSync(file, "utf8"); let m; importRegex.lastIndex = 0; while ((m = importRegex.exec(content)) !== null) { const p = m[1]; for (const legacy of LEGACY) { if (p.startsWith(legacy) || p === legacy) { violations.push({ file: rel, importPath: p, reason: "legacy healthcare path" }); } } for (const forbidden of FORBIDDEN) { if (p.startsWith(forbidden)) { violations.push({ file: rel, importPath: p, reason: "cross-module import" }); } } if (p.includes("../") && rel.startsWith("src/modules/healthcare")) { violations.push({ file: rel, importPath: p, reason: "relative import chain" }); } } } } for (const scope of ["src/shared", "components/ds"]) { const abs = join(ROOT, scope); if (!statSync(abs, { throwIfNotFound: false })?.isDirectory()) continue; for (const file of walk(abs)) { const rel = relative(ROOT, file).split(sep).join("/"); const content = readFileSync(file, "utf8"); let m; importRegex.lastIndex = 0; while ((m = importRegex.exec(content)) !== null) { if (m[1].startsWith("@/src/modules/healthcare/")) { violations.push({ file: rel, importPath: m[1], reason: "shared imports healthcare" }); } } } } console.log("Healthcare Import Validation\n"); if (violations.length) { console.error(`❌ ${violations.length} violation(s):`); for (const v of violations) console.error(` ${v.file}: ${v.importPath} (${v.reason})`); process.exit(1); } console.log("✅ Healthcare imports use @/src/modules/healthcare/*"); console.log("✅ No cross-module or relative import violations"); process.exit(0);