import fs from "node:fs/promises"; import path from "node:path"; const ROOT = process.cwd(); const TARGET_DIRS = [ path.join(ROOT, "apps", "bff", "src"), path.join(ROOT, "apps", "portal", "src"), ]; const FILE_EXTS = new Set([".ts", ".tsx"]); async function* walk(dir) { const entries = await fs.readdir(dir, { withFileTypes: true }); for (const e of entries) { const p = path.join(dir, e.name); if (e.isDirectory()) { if (e.name === "node_modules" || e.name === "dist" || e.name.startsWith(".")) continue; yield* walk(p); } else if (e.isFile()) { if (FILE_EXTS.has(path.extname(e.name))) yield p; } } } function replaceProvidersDeepImports(code) { // "@customer-portal/domain//providers/" -> "...//providers" return code .replaceAll( /from\s+"@customer-portal\/domain\/([a-z-]+)\/providers\/[^"]+"/g, 'from "@customer-portal/domain/$1/providers"' ) .replaceAll( /from\s+'@customer-portal\/domain\/([a-z-]+)\/providers\/[^']+'/g, "from '@customer-portal/domain/$1/providers'" ); } function replaceCommonProviderTypes(code) { // Move provider response wrapper types out of common root -> common/providers // Only touches *type-only* imports to avoid moving runtime exports accidentally. return code .replaceAll( /import\s+type\s+\{([^}]*)\}\s+from\s+"@customer-portal\/domain\/common";/g, (m, spec) => { const s = String(spec); const needsMove = s.includes("WhmcsResponse") || s.includes("WhmcsErrorResponse") || s.includes("SalesforceResponse"); if (!needsMove) return m; return `import type {${spec}} from "@customer-portal/domain/common/providers";`; } ) .replaceAll( /import\s+type\s+\{([^}]*)\}\s+from\s+'@customer-portal\/domain\/common';/g, (m, spec) => { const s = String(spec); const needsMove = s.includes("WhmcsResponse") || s.includes("WhmcsErrorResponse") || s.includes("SalesforceResponse"); if (!needsMove) return m; return `import type {${spec}} from '@customer-portal/domain/common/providers';`; } ); } function replaceProvidersNamespaceImports(code) { // import { Providers } from "@customer-portal/domain/"; // import { Providers as Foo } from "@customer-portal/domain/"; return code .replaceAll( /import\s+\{\s*Providers\s*\}\s+from\s+"@customer-portal\/domain\/([a-z-]+)";/g, 'import * as Providers from "@customer-portal/domain/$1/providers";' ) .replaceAll( /import\s+\{\s*Providers\s+as\s+([A-Za-z_$][A-Za-z0-9_$]*)\s*\}\s+from\s+"@customer-portal\/domain\/([a-z-]+)";/g, 'import * as $1 from "@customer-portal/domain/$2/providers";' ) .replaceAll( /import\s+\{\s*Providers\s*\}\s+from\s+'@customer-portal\/domain\/([a-z-]+)';/g, "import * as Providers from '@customer-portal/domain/$1/providers';" ) .replaceAll( /import\s+\{\s*Providers\s+as\s+([A-Za-z_$][A-Za-z0-9_$]*)\s*\}\s+from\s+'@customer-portal\/domain\/([a-z-]+)';/g, "import * as $1 from '@customer-portal/domain/$2/providers';" ); } function replaceToolkitPaginationHelper(code) { if (!code.includes("@customer-portal/domain/toolkit/validation/helpers")) return code; let next = code; next = next .replaceAll( /import\s+\{\s*createPaginationSchema\s*\}\s+from\s+"@customer-portal\/domain\/toolkit\/validation\/helpers";/g, 'import { Validation } from "@customer-portal/domain/toolkit";' ) .replaceAll( /import\s+\{\s*createPaginationSchema\s*\}\s+from\s+'@customer-portal\/domain\/toolkit\/validation\/helpers';/g, "import { Validation } from '@customer-portal/domain/toolkit';" ); // Update call sites next = next.replaceAll(/\bcreatePaginationSchema\b/g, "Validation.createPaginationSchema"); return next; } function transform(code) { let next = code; next = replaceProvidersDeepImports(next); next = replaceCommonProviderTypes(next); next = replaceProvidersNamespaceImports(next); next = replaceToolkitPaginationHelper(next); return next; } let changedFiles = 0; for (const dir of TARGET_DIRS) { for await (const file of walk(dir)) { const before = await fs.readFile(file, "utf8"); const after = transform(before); if (after !== before) { await fs.writeFile(file, after, "utf8"); changedFiles += 1; } } } console.log(`codemod-domain-imports: updated ${changedFiles} file(s)`);