import path from "node:path" ;
import { describe, expect, it } from "vitest" ;
import { analyzeTopology } from "../../scripts/lib/ts-topology/analyze.js" ;
import { renderTextReport } from "../../scripts/lib/ts-topology/reports.js" ;
import { createFilesystemPublicSurfaceScope } from "../../scripts/lib/ts-topology/scope.js" ;
import { main } from "../../scripts/ts-topology.ts" ;
import { createCapturedIo } from "../helpers/captured-io.js" ;
const repoRoot = path.join(process.cwd(), "test" , "fixtures" , "ts-topology" , "basic" );
function buildFixtureScope() {
return createFilesystemPublicSurfaceScope(repoRoot, {
id: "custom" ,
entrypointRoot: "src/public" ,
importPrefix: "fixture-sdk" ,
});
}
describe("ts-topology" , () => {
it("collapses canonical symbols exported by multiple public subpaths" , () => {
const envelope = analyzeTopology({
repoRoot,
scope: buildFixtureScope(),
report: "public-surface-usage" ,
});
const sharedThing = envelope.records.find((record) =>
record.exportNames.includes("sharedThing" ),
);
expect(sharedThing).toMatchObject({
declarationPath: "src/lib/shared.ts" ,
declarationLine: 1 ,
productionExtensions: ["alpha" , "beta" ],
productionPackages: ["src" ],
productionOwners: ["extension:alpha" , "extension:beta" , "src" ],
});
expect(sharedThing?.publicSpecifiers).toEqual(["fixture-sdk" , "fixture-sdk/extra" ]);
});
it("counts renamed imports, namespace imports, type-only imports, and test-only consumers" , () => {
const envelope = analyzeTopology({
repoRoot,
scope: buildFixtureScope(),
report: "public-surface-usage" ,
});
const aliasedThing = envelope.records.find((record) =>
record.exportNames.includes("aliasedThing" ),
);
const sharedType = envelope.records.find((record) => record.exportNames.includes("SharedType" ));
const testOnlyThing = envelope.records.find((record) =>
record.exportNames.includes("testOnlyThing" ),
);
expect(aliasedThing?.productionRefCount).toBe(1 );
expect(sharedType).toMatchObject({
isTypeOnlyCandidate: true ,
productionExtensions: ["alpha" , "beta" ],
productionRefCount: 2 ,
});
expect(testOnlyThing).toMatchObject({
productionRefCount: 0 ,
testRefCount: 1 ,
testConsumers: ["tests/public.test.ts" ],
});
});
it("surfaces single-owner shared and unused reports correctly" , () => {
const singleOwner = analyzeTopology({
repoRoot,
scope: buildFixtureScope(),
report: "single-owner-shared" ,
});
const unused = analyzeTopology({
repoRoot,
scope: buildFixtureScope(),
report: "unused-public-surface" ,
});
expect(singleOwner.records.map((record) => record.exportNames[0 ])).toContain(
"singleOwnerHelper" ,
);
expect(singleOwner.records.map((record) => record.exportNames[0 ])).not.toContain("sharedThing" );
expect(unused.records.map((record) => record.exportNames[0 ])).toEqual(["unusedThing" ]);
});
it("renders stable text summaries for the public-surface report" , () => {
const envelope = analyzeTopology({
repoRoot,
scope: buildFixtureScope(),
report: "public-surface-usage" ,
limit: 3 ,
});
expect(renderTextReport(envelope, 3 )).toMatchInlineSnapshot(`
"Scope: custom
Public exports analyzed: 6
Production-used exports: 4
Single-owner shared exports: 2
Unused public exports: 1
Top 2 candidate-to-move exports:
- fixture-sdk:aliasedThing -> src/lib/shared.ts:9 (prodRefs=1 , owners=extension:alpha, sharedness=35 , move=85 )
- fixture-sdk:singleOwnerHelper -> src/lib/shared.ts:5 (prodRefs=1 , owners=extension:alpha, sharedness=35 , move=85 )
Top 1 duplicated public exports:
- fixture-sdk:sharedThing via fixture-sdk, fixture-sdk/extra (src/lib/shared.ts:1 )"
`);
});
it("emits stable JSON and filtered report output through the CLI" , async () => {
const captured = createCapturedIo();
const jsonExit = await main(
[
"--scope=custom" ,
"--entrypoint-root=src/public" ,
"--import-prefix=fixture-sdk" ,
"--repo-root=test/fixtures/ts-topology/basic" ,
"--report=single-owner-shared" ,
"--json" ,
],
captured.io,
);
expect(jsonExit).toBe(0 );
const payload = JSON.parse(captured.readStdout());
expect(payload.report).toBe("single-owner-shared" );
expect(
payload.records.map((record: { exportNames: string[] }) => record.exportNames[0 ]),
).toEqual(["aliasedThing" , "singleOwnerHelper" ]);
const textCaptured = createCapturedIo();
const textExit = await main(
[
"--scope=custom" ,
"--entrypoint-root=src/public" ,
"--import-prefix=fixture-sdk" ,
"--repo-root=test/fixtures/ts-topology/basic" ,
"--report=consumer-topology" ,
"--limit=2" ,
],
textCaptured.io,
);
expect(textExit).toBe(0 );
expect(textCaptured.readStdout()).toMatchInlineSnapshot(`
"Scope: custom
Records with consumers: 5
Top 2 consumer-topology records:
- fixture-sdk:sharedThing prod=3 test=0 internal=0
- fixture-sdk:SharedType prod=2 test=0 internal=0
"
`);
});
});
Messung V0.5 in Prozent C=100 H=100 G=100
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet am 2026-06-10)
¤
*© Formatika GbR, Deutschland