import { readdir, readFile } from 'node:fs/promises'; import path from 'node:path'; interface BoundaryViolation { file: string; reason: string; } const sourceRoots = ['src', 'scripts', 'tests']; const commonJsPattern = new RegExp( String.raw`\b(${['require\\s*\\(', 'module\\.exports', 'exports\\.'].join('|')})`, ); async function collectFiles(dir: string): Promise { const entries = await readdir(dir, { withFileTypes: true }); const nestedFiles = await Promise.all( entries.map(async (entry) => { const fullPath = path.join(dir, entry.name); if (entry.isDirectory()) { if (entry.name === 'node_modules' || entry.name === 'dist') return []; return collectFiles(fullPath); } return [fullPath]; }), ); return nestedFiles.flat(); } function toProjectPath(filePath: string): string { return path.relative(process.cwd(), filePath).split(path.sep).join('/'); } function isHistoricalMigration(projectPath: string): boolean { return projectPath.startsWith('src/db/migrations/') && projectPath.endsWith('.js'); } function checkJsBoundary(projectPath: string): BoundaryViolation | null { if (isHistoricalMigration(projectPath)) return null; return { file: projectPath, reason: 'Unexpected JavaScript source. Use TypeScript ESM source.', }; } function checkTsBoundary(projectPath: string, source: string): BoundaryViolation | null { if (!commonJsPattern.test(source)) return null; return { file: projectPath, reason: 'Unexpected CommonJS syntax in TypeScript source. Use ESM import/export.', }; } async function checkFile(filePath: string): Promise { const projectPath = toProjectPath(filePath); const ext = path.extname(filePath); if (ext !== '.js' && ext !== '.ts') return null; const source = await readFile(filePath, 'utf8'); if (ext === '.js') { return checkJsBoundary(projectPath); } return checkTsBoundary(projectPath, source); } async function main(): Promise { const files = ( await Promise.all(sourceRoots.map((root) => collectFiles(path.join(process.cwd(), root)))) ).flat(); const checks = await Promise.all(files.map((file) => checkFile(file))); const violations = checks.filter((violation) => violation !== null); if (violations.length > 0) { console.error('ESM boundary check failed:'); for (const violation of violations) { console.error(`- ${violation.file}: ${violation.reason}`); } process.exitCode = 1; return; } console.log('ESM boundary check passed.'); } void main();