template/script/check-data-circular.ts

66 lines
2.0 KiB
TypeScript

import madge from 'madge';
import { relative, resolve } from 'node:path';
const root = process.cwd();
const entries = [
'packages-user/data-common/src/index.ts',
'packages-user/data-base/src/index.ts',
'packages-user/data-system/src/index.ts',
'packages-user/data-state/src/index.ts',
'packages/common/src/index.ts'
];
const dataPrefixes = [
'packages-user/data-common/',
'packages-user/data-base/',
'packages-user/data-system/',
'packages-user/data-state/'
];
function toRelativePath(file: string): string {
const absolute = resolve(root, file);
return relative(root, absolute).replaceAll('\\', '/');
}
function isInScope(cycle: readonly string[]): boolean {
return cycle.some(file => {
const normalized = file.replaceAll('\\', '/');
return (
normalized.includes('packages/common/') ||
dataPrefixes.some(prefix => normalized.includes(prefix))
);
});
}
const graph = await madge(entries, {
baseDir: root,
tsConfig: resolve(root, 'tsconfig.json'),
fileExtensions: ['ts', 'tsx'],
detectiveOptions: { ts: { skipTypeImports: false } }
});
const cycles = graph.circular() as readonly (readonly string[])[];
const inScope = cycles.filter(isInScope);
const outsideScope = cycles.filter(cycle => !isInScope(cycle));
console.log(
`Circular diagnostics: ${cycles.length} total, ${inScope.length} in-scope, ${outsideScope.length} outside scope`
);
for (const [index, cycle] of inScope.entries()) {
console.log(`IN-SCOPE CYCLE ${index + 1}:`);
console.log(cycle.map(toRelativePath).join(' -> '));
}
for (const [index, cycle] of outsideScope.entries()) {
console.log(`OUTSIDE-SCOPE CYCLE ${index + 1}:`);
console.log(cycle.map(toRelativePath).join(' -> '));
}
if (inScope.length > 0) {
console.error(
'Circular gate failed: four-package, common/data-common, or transitive common cycles remain'
);
process.exit(1);
}
console.log(
'Circular gate passed: four data packages and the transitive common boundary are acyclic'
);