core/loaders/code.loader.ts
core/loaders/code.loader.ts is a file in GovLab Stats. 72 lines of code and 15 definitions.
import type { AnalysisStats, ModuleAnalysis } from "#types/code.types";
import { arrayField, field, numberField, stringField } from "#core/selectors/field.selector";
import { ANALYSIS_SEARCH_DEPTH } from "#configuration/constants/code.constants";
import type { PathExclusion } from "@govlab/quality/config";
import { TOP_MODULES } from "#configuration/constants/metric.constants";
import { isRecord } from "#core/predicates/record.predicate";
import path from "node:path";
import { readJson } from "#core/loaders/data.loader";
import { readdirSafe } from "#core/loaders/folder.loader";
import { relativePath } from "@ssot/paths";
const findAnalysisFiles = function findAnalysisFiles(absDir: string, depth: number, ignore: PathExclusion): string[] {
const hexDir = relativePath("moduleInfo.root");
const entries = readdirSafe(absDir);
const here = entries.some((entry) => entry.isDirectory() && entry.name === hexDir)
? [path.join(absDir, relativePath("moduleInfo.analysis"))]
: [];
if (depth > ANALYSIS_SEARCH_DEPTH) {
return here;
}
const deeper = entries
.filter((entry) => entry.isDirectory() && entry.name !== hexDir)
.map((entry) => path.join(absDir, entry.name))
.filter((dir) => !ignore(dir))
.flatMap((dir) => findAnalysisFiles(dir, depth + 1, ignore));
return [...here, ...deeper];
};
const readModuleAnalysis = function readModuleAnalysis(file: string): ModuleAnalysis | null {
const data = readJson(file);
if (!isRecord(data)) {
return null;
}
const summary = field(data, "summary");
return {
definitions: numberField(summary, "definitions", 0),
edges: numberField(summary, "edges", 0),
flows: arrayField(data, "definitions")
.map((definition) => stringField(definition, "flow"))
.filter((flow) => flow.length > 0),
module: stringField(data, "module"),
unresolvedCalls: numberField(summary, "unresolvedCalls", 0),
};
};
const flowCounts = function flowCounts(modules: readonly ModuleAnalysis[]): Map<string, number> {
const counts = new Map<string, number>();
for (const flow of modules.flatMap((module) => module.flows)) {
counts.set(flow, (counts.get(flow) ?? 0) + 1);
}
return counts;
};
const sumOf = function sumOf(modules: readonly ModuleAnalysis[], pick: (module: ModuleAnalysis) => number): number {
return modules.reduce((total, module) => total + pick(module), 0);
};
export const collectAnalysis = function collectAnalysis(root: string, ignore: PathExclusion): AnalysisStats {
const modules = findAnalysisFiles(root, 0, ignore)
.map((file) => readModuleAnalysis(file))
.filter((module): module is ModuleAnalysis => module !== null);
const edges = sumOf(modules, (module) => module.edges);
const unresolvedCalls = sumOf(modules, (module) => module.unresolvedCalls);
return {
byFlow: flowCounts(modules),
definitions: sumOf(modules, (module) => module.definitions),
edges,
modulesAnalyzed: modules.length,
producer: relativePath("govlab.patterns"),
resolutionRate: edges > 0 ? (edges - unresolvedCalls) / edges : 0,
topByDefinitions: modules
.toSorted((left, right) => right.definitions - left.definitions)
.slice(0, TOP_MODULES)
.map((module) => ({ definitions: module.definitions, module: module.module })),
unresolvedCalls,
};
};