import { readdir, readFile } from "node:fs/promises"; import path from "node:path"; const APP_DIR = path.resolve("apps/sn/app"); const POPUP_DEFINITIONS = path.resolve( "apps/sn/src/features/windowPopup/model/popupDefinitions.ts", ); const POPUP_REGISTRY = path.resolve( "apps/sn/src/features/windowPopup/registry.tsx", ); const routeAliases = new Set([ "/map/agedPipePopup", "/map2", "/html/aged-pipe", "/html/map", ]); const developmentRoutes = new Set([ "/ii/fileupload", "/ii/test", "/oper/operStore", ]); const infrastructureRoutes = new Set(["/", "/popup/[popupKey]"]); async function collectPageFiles(directory) { const entries = await readdir(directory, { withFileTypes: true }); const nested = await Promise.all( entries.map(async (entry) => { const absolutePath = path.join(directory, entry.name); if (entry.isDirectory()) return collectPageFiles(absolutePath); return entry.name === "page.tsx" ? [absolutePath] : []; }), ); return nested.flat(); } function toRoute(filePath) { const relative = path.relative(APP_DIR, path.dirname(filePath)); const segments = relative .split(path.sep) .filter(Boolean) .filter((segment) => !/^\(.+\)$/.test(segment)); return `/${segments.join("/")}`.replace(/\/$/, "") || "/"; } function extractDefinitionKeys(source) { const objectStart = source.indexOf("export const windowPopupDefinitions = {"); const objectEnd = source.indexOf("} as const satisfies", objectStart); return [...source.slice(objectStart, objectEnd).matchAll(/^ ([A-Za-z][A-Za-z0-9]*): \{/gm)] .map((match) => match[1]); } function extractRegistryKeys(source) { const objectStart = source.indexOf("export const windowPopupRegistry = {"); const objectEnd = source.indexOf("} satisfies", objectStart); return [...source.slice(objectStart, objectEnd).matchAll(/^ ([A-Za-z][A-Za-z0-9]*):/gm)] .map((match) => match[1]); } const pageFiles = await collectPageFiles(APP_DIR); const routes = pageFiles.map(toRoute).sort(); const definitions = extractDefinitionKeys( await readFile(POPUP_DEFINITIONS, "utf8"), ); const registry = extractRegistryKeys(await readFile(POPUP_REGISTRY, "utf8")); const popupMissingInRegistry = definitions.filter((key) => !registry.includes(key)); const registryMissingInDefinitions = registry.filter( (key) => !definitions.includes(key), ); const routeCategory = (route) => { if (route === "/auth/login") return "authentication"; if (routeAliases.has(route)) return "alias-or-preview"; if (developmentRoutes.has(route)) return "development"; if (infrastructureRoutes.has(route)) return "infrastructure"; return "main-content"; }; const routeCounts = routes.reduce((groups, route) => { const category = routeCategory(route); groups[category] ??= []; groups[category].push(route); return groups; }, {}); const report = { routeFileCount: routes.length, routeCounts: Object.fromEntries( Object.entries(routeCounts).map(([key, value]) => [key, value.length]), ), routes: routes.map((route) => ({ route, category: routeCategory(route) })), popupDefinitionCount: definitions.length, popupRegistryCount: registry.length, popupMissingInRegistry, registryMissingInDefinitions, popupKeys: definitions, }; console.log(JSON.stringify(report, null, 2)); if (popupMissingInRegistry.length || registryMissingInDefinitions.length) { process.exitCode = 1; }