TorbatYar/frontend/scripts/validate-accounting-circular.mjs
Mortezakoohjani 6f4a484051 Migrate Beauty, Healthcare, and Accounting frontend to modular src/modules architecture.
Move business logic out of App Router into module packages, add boundary validation scripts, and keep all routes as thin re-exports without changing URLs or API behavior.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-26 22:28:27 +03:30

76 lines
2.2 KiB
JavaScript

#!/usr/bin/env node
/** Detect circular imports within src/modules/accounting. */
import { readFileSync, readdirSync, statSync } from "node:fs";
import { join, relative, sep, dirname } from "node:path";
import { fileURLToPath } from "node:url";
const ROOT = join(fileURLToPath(new URL(".", import.meta.url)), "..");
const MOD = join(ROOT, "src", "modules", "accounting");
const EXT = new Set([".ts", ".tsx"]);
const importRe = /from\s+["'](@\/src\/modules\/accounting\/[^"']+)["']/g;
function walk(dir, files = []) {
for (const entry of readdirSync(dir)) {
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 {Map<string, string[]>} */
const graph = new Map();
for (const file of walk(MOD)) {
const rel = relative(MOD, file).split(sep).join("/").replace(/\.tsx?$/, "");
const content = readFileSync(file, "utf8");
const deps = [];
let m;
importRe.lastIndex = 0;
while ((m = importRe.exec(content)) !== null) {
const p = m[1].replace("@/src/modules/accounting/", "");
deps.push(p);
}
graph.set(rel, deps);
}
function findCycles() {
/** @type {string[][]} */
const cycles = [];
const visiting = new Set();
const visited = new Set();
/** @type {string[]} */
const stack = [];
function dfs(node) {
if (visiting.has(node)) {
const idx = stack.indexOf(node);
cycles.push(stack.slice(idx).concat(node));
return;
}
if (visited.has(node)) return;
visiting.add(node);
stack.push(node);
for (const dep of graph.get(node) ?? []) {
const key = dep.replace(/\/index$/, "");
if (graph.has(key)) dfs(key);
}
stack.pop();
visiting.delete(node);
visited.add(node);
}
for (const node of graph.keys()) dfs(node);
return cycles;
}
const cycles = findCycles();
console.log("Circular Dependency Check (accounting module)\n");
if (cycles.length) {
console.error(`${cycles.length} cycle(s):`);
for (const c of cycles) console.error(` ${c.join(" → ")}`);
process.exit(1);
}
console.log(`✅ No cycles in ${graph.size} accounting module files`);
process.exit(0);