Files
frontend/apps/sn/scripts/audit-screen-inventory.mjs
T
2026-09-15 10:00:42 +09:00

101 lines
3.3 KiB
JavaScript

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",
]);
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;
}