TorbatYar/frontend/scripts/migrate-accounting-pages.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

86 lines
3.1 KiB
JavaScript

#!/usr/bin/env node
/** Move app/accounting page logic to src/modules/accounting/pages and leave thin route adapters. */
import {
readFileSync,
writeFileSync,
mkdirSync,
readdirSync,
statSync,
} from "node:fs";
import { join, dirname, relative, sep } from "node:path";
import { fileURLToPath } from "node:url";
const ROOT = join(fileURLToPath(new URL(".", import.meta.url)), "..");
const APP = join(ROOT, "app", "accounting");
const PAGES = join(ROOT, "src", "modules", "accounting", "pages");
function walkPages(dir, acc = []) {
for (const entry of readdirSync(dir)) {
const full = join(dir, entry);
if (statSync(full).isDirectory()) walkPages(full, acc);
else if (entry === "page.tsx") acc.push(full);
}
return acc;
}
function routeToExportName(routeRel) {
const base = routeRel.replace(/\/page\.tsx$/, "").replace(/^\.\/?/, "");
if (!base || base === "page.tsx") return "AccountingDashboardPage";
const segments = base.split("/").map((part) => {
if (part.startsWith("[") && part.endsWith("]")) {
const inner = part.slice(1, -1);
return inner.charAt(0).toUpperCase() + inner.slice(1);
}
return part
.split("-")
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
.join("");
});
return `${segments.join("")}Page`;
}
function moduleRelFromRoute(routePagePath) {
const relDir = relative(APP, dirname(routePagePath)).split(sep).join("/");
if (!relDir || relDir === ".") return "dashboard";
return relDir.replace(/\[(\w+)\]/g, "$1");
}
function convertToNamedExport(content, exportName) {
if (/export default function \w+/.test(content)) {
return content.replace(/export default function (\w+)/, "export function $1");
}
if (/export default function Page/.test(content)) {
return content.replace(/export default function Page/, `export function ${exportName}`);
}
if (/export default function/.test(content)) {
return content.replace(/export default function/, "export function");
}
return `${content}\nexport { ${exportName} };\n`;
}
function resolveExportName(content, fallback) {
const named = content.match(/export default function (\w+)/);
if (named && named[1] !== "Page") return named[1];
return fallback;
}
let moved = 0;
for (const pageFile of walkPages(APP)) {
const routeRel = relative(APP, pageFile).split(sep).join("/");
const moduleRel = moduleRelFromRoute(pageFile);
const fallbackName = routeToExportName(routeRel);
const original = readFileSync(pageFile, "utf8");
const exportName = resolveExportName(original, fallbackName);
const moduleContent = convertToNamedExport(original, exportName);
const targetPath = join(PAGES, `${moduleRel}.tsx`);
mkdirSync(dirname(targetPath), { recursive: true });
writeFileSync(targetPath, moduleContent, "utf8");
const importPath = `@/src/modules/accounting/pages/${moduleRel}`;
const thin = `"use client";\n\nexport { ${exportName} as default } from "${importPath}";\n`;
writeFileSync(pageFile, thin, "utf8");
moved++;
}
console.log(`Migrated ${moved} accounting pages to src/modules/accounting/pages/`);