core/coordinators/anatomy.coordinator.ts
core/coordinators/anatomy.coordinator.ts is a file in Bane's Lab Build Scripts. 132 lines of code and 27 definitions.
import {
ANATOMY_TREE_LABEL,
anatomyLine,
lostReference,
lostReferencesLine,
treeSummaryLine,
unlabeledTree,
} from "#configuration/strings/anatomy.strings";
import type { AnatomyScope, DerivedTree, ReferenceSite, TreeOptions } from "#types/anatomy.types";
import type { AnatomySnapshot, AnatomyTreeDeclaration } from "@banes-lab/web/types/anatomy.types.js";
import { ROOT, absolutePath, relativePath } from "@ssot/paths";
import { buildContext, buildModuleReport, discoverModules } from "@govlab/patterns";
import { convertAnatomy, countFolders, referencedTrees, withRepository } from "#core/converters/anatomy.converter";
import { currentCandidates, exactReferencesOf, lostReferences } from "#core/converters/baseline.converter";
import { documentsOf, filesOf } from "#core/converters/document.converter";
import { persistBaseline, readBaseline } from "#core/persistence/baseline.persistence";
import { readDeclaredTree, treeDeclarations } from "#core/loaders/tree.loader";
import { renderAnatomy, renderAnatomyMeasures } from "#core/formatters/anatomy.formatter";
import { TREE_TAB } from "@banes-lab/web/ids/anatomy.ids";
import { UNQUALIFIED } from "#configuration/constants/anatomy.constants";
import { anatomyMeasures } from "#core/converters/metric.converter";
import { chartsOf } from "#core/converters/figure.converter";
import { createWalkAssets } from "#core/factories/walk.factory";
import { docsHostFor } from "@govlab/docs";
import { excludeMatcher } from "@govlab/quality/config";
import { extractConfigs } from "#core/coordinators/config.coordinator";
import { importEdgesOf } from "#core/converters/specifier.converter";
import { join } from "node:path";
import { persistTreeDeclarations } from "#core/persistence/tree.persistence";
import process from "node:process";
import { qualifiedSnapshot } from "#core/converters/snapshot.converter";
import { qualifyWalks } from "#core/converters/walk.converter";
import { replaceFolderFiles } from "#core/persistence/asset.persistence";
import { withInherited } from "#core/loaders/heritage.loader";
import { writeCanonicalText } from "@govlab/canonical-write";
const deriveTree = async function deriveTree(
source: AnatomyTreeDeclaration,
scope: AnatomyScope,
): Promise<AnatomySnapshot> {
const moduleDir = join(ROOT, source.folder);
const report = { pruned: scope.pruned, root: ROOT };
const { built, entries } = await buildModuleReport(moduleDir, scope.context, report);
const tree = withInherited(moduleDir, readDeclaredTree(source, scope.pruned));
const charts = await chartsOf(moduleDir, source.tab === TREE_TAB ? ANATOMY_TREE_LABEL : source.tab);
const documents = documentsOf(tree, scope.host, moduleDir);
const imports =
scope.options.specifiers === null ? [] : await importEdgesOf(filesOf(tree), scope.options.specifiers);
const snapshot = convertAnatomy(
{
cells: built.cells,
charts,
documents,
entries,
generatedSources: source.generatedSources,
imports,
report: built.report,
tree,
vectors: built.svgs,
},
scope.assets,
);
const { repository, tab } = source;
const linked =
repository === null
? snapshot
: { ...snapshot, tree: withRepository(snapshot.tree, repository, source.generatedSources) };
if (tab === TREE_TAB) {
return linked;
}
const qualify = function qualify(path: string): string {
return scope.options.qualifyPath(tab, path);
};
qualifyWalks(linked.tree, qualify, scope.assets);
return qualifiedSnapshot(linked, qualify);
};
export const buildAnatomy = async function buildAnatomy(
options: TreeOptions = UNQUALIFIED,
): Promise<readonly DerivedTree[]> {
await extractConfigs();
const pruned = await excludeMatcher(ROOT);
const scope: AnatomyScope = {
assets: createWalkAssets(),
context: await buildContext(discoverModules(ROOT, pruned), { pruned, root: ROOT }),
host: await docsHostFor(ROOT),
options,
pruned,
};
const declarations = treeDeclarations();
await persistTreeDeclarations(declarations);
const derived = await declarations.reduce<Promise<readonly DerivedTree[]>>(async (previous, source) => {
const done = await previous;
const snapshot = await deriveTree(source, scope);
return [...done, { exportName: source.exportName, snapshot, tab: source.tab }];
}, Promise.resolve([]));
const sites = new Map<string, ReferenceSite>();
const trees = referencedTrees(derived, options, scope.assets, sites);
const baselineFile = absolutePath("app.baseline");
const lost = lostReferences(readBaseline(baselineFile), sites);
if (lost.length > 0) {
const lines = lost.map((entry) =>
lostReference(entry.path, entry.channel, entry.text, currentCandidates(entry, sites)),
);
throw new Error(lostReferencesLine(lines));
}
await persistBaseline(baselineFile, exactReferencesOf(sites));
await writeCanonicalText(absolutePath("app.anatomy"), renderAnatomy(trees));
const labels = new Map(declarations.map((declaration) => [declaration.tab, declaration.label]));
const measures = anatomyMeasures(trees, (tab) => {
const label = labels.get(tab);
if (label === undefined) {
throw new Error(unlabeledTree(tab));
}
return label;
});
await writeCanonicalText(absolutePath("app.anatomyMeasures"), renderAnatomyMeasures(measures));
replaceFolderFiles(absolutePath("app.walks"), scope.assets.walks);
replaceFolderFiles(absolutePath("app.sources"), scope.assets.sources);
return trees;
};
export const deriveAnatomy = async function deriveAnatomy(options: TreeOptions = UNQUALIFIED): Promise<void> {
const trees = await buildAnatomy(options);
process.stdout.write(
anatomyLine(
trees.map((tree) => treeSummaryLine(tree, countFolders(tree.snapshot.tree))),
{
anatomy: relativePath("app.anatomy"),
sources: relativePath("app.sources"),
walks: relativePath("app.walks"),
},
),
);
};