# core/converters/document.converter.ts

> 55 lines of code and 11 definitions.

Tree: Bane's Lab Build Scripts
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/build-scripts#file-build-scripts-core-converters-document-converter-ts
Source text: https://banes-lab.com/source/build-scripts/core/converters/document.converter.ts.txt

Listed in [core/converters](https://banes-lab.com/api/source/build-scripts/core/converters.md), after [core/converters/diagram.converter.ts](https://banes-lab.com/source/build-scripts/core/converters/diagram.converter.ts.md) and before [core/converters/figure.converter.ts](https://banes-lab.com/source/build-scripts/core/converters/figure.converter.ts.md).

## Definitions

- `documentOf` (lexical_declaration, line 35, exported)
- `headingCount` (lexical_declaration, line 25)
- `filesOf` (lexical_declaration, line 31, exported)
- `documentsOf` (lexical_declaration, line 51, exported)
- `DOCUMENT_SUFFIX` (lexical_declaration, line 18)
- `HEADING_MARK` (lexical_declaration, line 19)
- `isDocument` (lexical_declaration, line 21, exported)
- `lines` (lexical_declaration, line 26)
- `mask` (lexical_declaration, line 27)
- `meta` (lexical_declaration, line 36, exported)
- `findings` (lexical_declaration, line 37, exported)

## Contained in

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

## Linked from

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

## Source

```typescript
import type { DiskFile, DiskFolder } from "#types/structure.types";
import {
    analyzeSync,
    bodyStart,
    codeLineMask,
    describeFindings,
    docMeta,
    isBoundaryFilename,
    mermaidBlocks,
    pathReferences,
    refScanOf,
    splitLines,
} from "@govlab/docs";
import type { DocsHost } from "@govlab/docs";
import type { DocumentView } from "@banes-lab/web/types/anatomy.types.js";
import { join } from "node:path";

const DOCUMENT_SUFFIX = ".md";
const HEADING_MARK = "#";

export const isDocument = function isDocument(file: DiskFile): boolean {
    return file.name.endsWith(DOCUMENT_SUFFIX);
};

const headingCount = function headingCount(source: string): number {
    const lines = splitLines(source);
    const mask = codeLineMask(lines, bodyStart(lines));
    return lines.filter((line, index) => !mask[index] && line.startsWith(HEADING_MARK)).length;
};

export const filesOf = function filesOf(folder: DiskFolder): DiskFolder["files"] {
    return [...folder.files, ...folder.folders.flatMap(filesOf)];
};

export const documentOf = function documentOf(host: DocsHost, memberDir: string, file: DiskFile): DocumentView {
    const meta = docMeta(host.ctx, join(memberDir, ...file.path.split("/")), file.text);
    const findings = describeFindings(analyzeSync(host.ctx, meta));
    return {
        boundary: meta.hostBoundary || isBoundaryFilename(meta.docFilename),
        concern: meta.fmConcern.length === 0 ? null : meta.fmConcern,
        constructs: refScanOf(host.ctx, meta).constructs.length,
        fields: Object.keys(meta.fields),
        findings,
        form: meta.fmType ?? null,
        headings: headingCount(file.text),
        mermaid: mermaidBlocks(file.text).length,
        paths: pathReferences(file.text).length,
    };
};

export const documentsOf = function documentsOf(
    tree: DiskFolder,
    host: DocsHost,
    moduleDir: string,
): Map<string, DocumentView> {
    return new Map(
        filesOf(tree)
            .filter(isDocument)
            .map((file) => [file.path, documentOf(host, moduleDir, file)] as const),
    );
};
```
