# test.build/core/converters/source.converter.test.ts

> 185 lines of code and 0 definitions.

Tree: Codebase Testing
Language: typescript
Canonical: https://banes-lab.com/anatomy/testing#file-testing-test-build-core-converters-source-converter-test-ts
Source text: https://banes-lab.com/source/testing/test.build/core/converters/source.converter.test.ts.txt

Listed in [test.build/core/converters](https://banes-lab.com/api/source/testing/test.build/core/converters.md), after [test.build/core/converters/snapshot.converter.test.ts](https://banes-lab.com/source/testing/test.build/core/converters/snapshot.converter.test.ts.md) and before [test.build/core/converters/source.route.converter.test.ts](https://banes-lab.com/source/testing/test.build/core/converters/source.route.converter.test.ts.md).

## Contained in

- [test.build/core/converters](https://banes-lab.com/anatomy/testing/folder-testing-test-build-core-converters.md)

## Source

````typescript
import type { AnatomyFile, AnatomyFolder, AnatomySnapshot } from "@banes-lab/web/types/anatomy.types.js";
import {
    canonicalOf,
    sourceFiles,
    sourceLeaves,
    summaryOf,
} from "@banes-lab/build-scripts/core/converters/source.converter.ts";
import { describe, expect, it } from "vitest";
import type { SourceTools } from "@banes-lab/build-scripts/types/source.types.ts";
import { createLinker } from "@banes-lab/build-scripts/core/resolvers/link.resolver.ts";
import { placementsOf } from "@banes-lab/build-scripts/core/converters/location.converter.ts";
import { sourceIndexPlans } from "@banes-lab/build-scripts/core/converters/index.source.converter.ts";

const SITE = "https://example.test";
const STATS = {
    bytes: 10,
    callable: 1,
    definitions: 1,
    edges: 0,
    exported: 1,
    files: 1,
    findings: {},
    flows: {},
    lines: { blank: 0, code: 3, total: 3 },
};

const SLUG_SAFE = "abcdefghijklmnopqrstuvwxyz0123456789";

const slug = function slug(path: string): string {
    let out = "";
    for (let at = 0; at < path.length; at += 1) {
        const char = path.charAt(at);
        out += SLUG_SAFE.includes(char) ? char : "-";
    }
    return out;
};

const lineSuffix = function lineSuffix(line: number | null): string {
    return line === null ? "" : `:${String(line)}`;
};

const TOOLS: SourceTools = {
    definitionAnchor: "definition-",
    fileId: (path) => `file-${slug(path)}`,
    languageOf: () => "typescript",
    lineInfix: ":",
    localPath: (path) => path.slice(path.indexOf("~") + 1),
    nodeHref: (path, line) => `/anatomy/build#file-${slug(path)}${lineSuffix(line)}`,
};

const file = function file(path: string, calls: readonly string[]): AnatomyFile {
    return {
        definitions: [
            {
                callable: true,
                callees: calls.map((target) => ({ file: target, id: `${target}#x`, name: "x" })),
                callers: [],
                exported: true,
                file: path,
                flow: "entry",
                id: `${path}#run`,
                inDegree: 0,
                kind: "function",
                line: 1,
                local: false,
                name: "run",
                outDegree: calls.length,
            },
        ],
        distribution: { invariants: [], variants: [] },
        document: null,
        findings: [],
        generated: false,
        id: path,
        inherited: false,
        layer: "processing",
        name: path.slice(path.lastIndexOf("/") + 1),
        path,
        slots: null,
        source: "source.a.generated.txt",
        stats: STATS,
        walk: null,
    };
};

const folder = function folder(files: readonly AnatomyFile[]): AnatomyFolder {
    return {
        files,
        findings: [],
        folders: [],
        id: "root",
        layer: null,
        name: "",
        path: "",
        role: "member",
        stats: STATS,
        walk: null,
    };
};

const snapshot = function snapshot(files: readonly AnatomyFile[]): AnatomySnapshot {
    const metrics = {
        callable: 0,
        definitions: 0,
        edges: 0,
        exported: 0,
        findings: {},
        flows: {},
        maxInDegree: 0,
        maxOutDegree: 0,
        resolutionRate: 1,
        unresolvedCalls: 0,
    };
    return { charts: [], findings: [], imports: [], metrics, states: [], tree: folder(files), unresolvedCalls: [] };
};

const TREE = {
    label: "Build",
    snapshot: snapshot([file("build~core/a.ts", ["build~core/b.ts"]), file("build~core/b.ts", [])]),
    tab: "build",
};

describe("summaryOf", () => {
    it("counts a file's lines of code and definitions, with the plural only past one", () => {
        expect(summaryOf({ definitions: [], stats: STATS })).toBe("3 lines of code and 0 definitions.");
        expect(summaryOf(file("a.ts", []))).toBe("3 lines of code and 1 definition.");
    });
});

describe("canonicalOf", () => {
    it("drops a line from a file anchor and maps a definition anchor to its file", () => {
        const canonical = canonicalOf(SITE, TOOLS);
        expect(canonical(`${SITE}/anatomy/build#file-x:12`)).toBe("/anatomy/build#file-x");
        expect(canonical("/anatomy/build#definition-run:build~core/a.ts")).toBe("/anatomy/build#file-build-core-a-ts");
        expect(canonical("/pag/guide#setup")).toBe("/pag/guide#setup");
    });
});

describe("sourceFiles and sourceLeaves", () => {
    it("addresses every file by its member-relative path and links the files it uses and is used by", async () => {
        const files = sourceFiles([TREE], TOOLS);
        expect(files.map((entry) => entry.identity.address.json)).toStrictEqual([
            "/json/source/build/core/a.ts",
            "/json/source/build/core/b.ts",
        ]);
        const linker = createLinker(
            files.map((entry) => entry.identity),
            SITE,
            canonicalOf(SITE, TOOLS),
        );
        const placements = placementsOf(
            sourceIndexPlans(files, TOOLS.localPath).trees,
            files.map((entry) => entry.identity),
            SITE,
        );
        const leaves = await sourceLeaves(files, {
            checks: () => [],
            folder: (ref) =>
                ref === files[0]?.identity.ref
                    ? { containedIn: linker.link("root", files[1]?.identity.ref ?? ""), contains: [] }
                    : null,
            grounds: async () => {
                await Promise.resolve();
                return [];
            },
            linkedBy: (ref) =>
                ref === files[0]?.identity.ref ? [linker.link("core/b.ts", files[1]?.identity.ref ?? "")] : [],
            linker,
            placement: (ref) => placements.get(ref) ?? null,
            textOf: () => "export const run = 1;",
            tools: TOOLS,
        });
        expect(leaves[0]?.data).toMatchObject({
            definitions: [{ name: "run", signature: "export const run = 1;" }],
            path: "core/a.ts",
            relations: [
                { links: [{ label: "root" }], relation: "contained-in" },
                { links: [{ label: "core/b.ts", markdown: `${SITE}/source/build/core/b.ts.md` }], relation: "uses" },
                { links: [{ label: "core/b.ts" }], relation: "linked-from" },
            ],
            siblings: { next: { label: "core/b.ts" }, previous: null },
            text: `${SITE}/source/build/core/a.ts.txt`,
            tree: "build",
            up: { json: `${SITE}/json/api/source/build`, ref: "api:source/build" },
        });
        expect(leaves[0]?.markdown).toContain("## Uses");
        expect(leaves[1]?.data).toMatchObject({ siblings: { next: null, previous: { label: "core/a.ts" } } });
        expect(JSON.stringify(leaves[1]?.data)).not.toContain("contained-in");
        expect(leaves[0]?.text).toStrictEqual({
            address: "/source/build/core/a.ts.txt",
            body: "export const run = 1;",
        });
        expect(leaves[0]?.markdown).toContain(`Source text: ${SITE}/source/build/core/a.ts.txt`);
        expect(leaves[0]?.markdown).toContain("```typescript\nexport const run = 1;\n```");
        expect(JSON.stringify(leaves.map((leaf) => leaf.data))).not.toContain("build~");
    });
});
````
