# core/formatters/readme.formatter.ts

> 110 lines of code and 16 definitions.

Tree: GovLab Docs
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/docs#file-docs-core-formatters-readme-formatter-ts
Source text: https://banes-lab.com/source/docs/core/formatters/readme.formatter.ts.txt

Listed in [core/formatters](https://banes-lab.com/api/source/docs/core/formatters.md), after [core/formatters/markdown.formatter.ts](https://banes-lab.com/source/docs/core/formatters/markdown.formatter.ts.md) and before [core/formatters/readme.section.formatter.ts](https://banes-lab.com/source/docs/core/formatters/readme.section.formatter.ts.md).

## Definitions

- `section` (lexical_declaration, line 35)
- `generateModuleDoc` (lexical_declaration, line 113, exported)
- `customLayers` (lexical_declaration, line 102)
- `apiLayer` (lexical_declaration, line 51)
- `installLayer` (lexical_declaration, line 43)
- `useLayer` (lexical_declaration, line 59)
- `chartsLayer` (lexical_declaration, line 64)
- `coreLayers` (lexical_declaration, line 95)
- `repoLayer` (lexical_declaration, line 108)
- `CORE_FIELDS` (lexical_declaration, line 22, exported)
- `isRenderableSlot` (lexical_declaration, line 39)
- `install` (lexical_declaration, line 47)
- `toUse` (lexical_declaration, line 60)
- `LAYERS` (lexical_declaration, line 68)
- `body` (lexical_declaration, line 109)
- `parts` (lexical_declaration, line 114, exported)

## Contained in

- [core/formatters](https://banes-lab.com/anatomy/docs/folder-docs-core-formatters.md)

## Uses

- [configuration/strings/readme.strings.ts](https://banes-lab.com/source/docs/configuration/strings/readme.strings.ts.md)
- [core/converters/layer.converter.ts](https://banes-lab.com/source/docs/core/converters/layer.converter.ts.md)
- [core/formatters/markdown.formatter.ts](https://banes-lab.com/source/docs/core/formatters/markdown.formatter.ts.md)
- [core/formatters/readme.section.formatter.ts](https://banes-lab.com/source/docs/core/formatters/readme.section.formatter.ts.md)

## Used by

- [core/factories/document.factory.ts](https://banes-lab.com/source/docs/core/factories/document.factory.ts.md)

## Linked from

- [configuration/strings](https://banes-lab.com/anatomy/docs/folder-docs-configuration-strings.md)
- [core/converters](https://banes-lab.com/anatomy/docs/folder-docs-core-converters.md)
- [core/factories](https://banes-lab.com/anatomy/docs/folder-docs-core-factories.md)
- [core/formatters](https://banes-lab.com/anatomy/docs/folder-docs-core-formatters.md)

## Source

```typescript
import { METRICS_LAYER, REPO_METRICS_LAYER } from "#configuration/constants/layer.constants";
import { README_HEADINGS, README_TEXT, chartsNote, defaultInstall } from "#configuration/strings/readme.strings";
import type { ReadmeLayer, RenderContext } from "#types/readme.types";
import {
    renderApi,
    renderConcepts,
    renderDeps,
    renderDomains,
    renderMetrics,
    renderPrinciples,
    renderRepoMetrics,
} from "#core/formatters/readme.section.formatter";
import {
    renderConfiguration,
    renderQuickStart,
    renderRenderable,
    titleCase,
} from "#core/formatters/markdown.formatter";
import { relativePath } from "@ssot/paths";
import { wrapLayer } from "#core/converters/layer.converter";

export const CORE_FIELDS: ReadonlySet<string> = new Set([
    "overview",
    "whenToUse",
    "whenNotToUse",
    "quickStart",
    "configuration",
    "disposal",
    "aiContext",
    "apiNotes",
    "api",
    "install",
]);

const section = function section(heading: string, body: string): string {
    return `## ${heading}\n\n${body}`;
};

const isRenderableSlot = function isRenderableSlot(value: unknown): boolean {
    return typeof value === "string" || Array.isArray(value);
};

const installLayer = function installLayer(context: RenderContext): string {
    const body = isRenderableSlot(context.docs.install)
        ? renderRenderable(context.docs.install)
        : defaultInstall(context.scoped);
    const install = body.trim() === "" ? "" : `${section(README_HEADINGS.install, body)}\n\n`;
    return install + section(README_HEADINGS.quickStart, renderQuickStart(context.docs.quickStart));
};

const apiLayer = function apiLayer(context: RenderContext): string {
    if (context.surface.length > 0) {
        return section(README_HEADINGS.api, renderApi(context.surface, context.docs.apiNotes));
    }
    const body = isRenderableSlot(context.docs.api) ? renderRenderable(context.docs.api) : README_TEXT.noSurface;
    return section(README_HEADINGS.api, body);
};

const useLayer = function useLayer(context: RenderContext): string {
    const toUse = section(README_HEADINGS.whenToUse, renderRenderable(context.docs.whenToUse));
    return `${toUse}\n\n${section(README_HEADINGS.whenNotToUse, renderRenderable(context.docs.whenNotToUse))}`;
};

const chartsLayer = function chartsLayer(context: RenderContext): string | null {
    return context.hasCharts ? section(README_HEADINGS.charts, chartsNote(relativePath("moduleInfo.charts"))) : null;
};

const LAYERS: readonly ReadmeLayer[] = [
    { id: "overview", render: (context) => section(README_HEADINGS.purpose, renderRenderable(context.docs.overview)) },
    { id: "use", render: useLayer },
    { id: "charts", render: chartsLayer },
    { id: "install", render: installLayer },
    { id: "api", render: apiLayer },
    {
        id: "config",
        render: (context) => section(README_HEADINGS.configuration, renderConfiguration(context.docs.configuration)),
    },
    { id: "deps", render: (context) => section(README_HEADINGS.dependencies, renderDeps(context.pkg)) },
    {
        id: "ai-context",
        render: (context) => section(README_HEADINGS.aiContext, renderRenderable(context.docs.aiContext)),
    },
    { id: "domains", render: (context) => (context.domains.length > 0 ? renderDomains(context.domains) : null) },
    {
        id: "principles",
        render: (context) => (context.principles.length > 0 ? renderPrinciples(context.principles) : null),
    },
    {
        id: "quality-governance",
        render: (context) => (context.concepts.length > 0 ? renderConcepts(context.concepts) : null),
    },
    { id: "disposal", render: (context) => section(README_HEADINGS.disposal, renderRenderable(context.docs.disposal)) },
];

const coreLayers = function coreLayers(context: RenderContext): string[] {
    return LAYERS.flatMap((layer) => {
        const body = layer.render(context);
        return body === null || body === "" ? [] : [wrapLayer(layer.id, body)];
    });
};

const customLayers = function customLayers(context: RenderContext): string[] {
    return Object.keys(context.docs)
        .filter((key) => !CORE_FIELDS.has(key))
        .map((key) => wrapLayer(key, section(titleCase(key), renderRenderable(context.docs[key]))));
};

const repoLayer = function repoLayer(context: RenderContext): string[] {
    const body = renderRepoMetrics(context.repo);
    return body === "" ? [] : [wrapLayer(REPO_METRICS_LAYER, body)];
};

export const generateModuleDoc = function generateModuleDoc(context: RenderContext): string {
    const parts = [
        `# ${context.scoped}`,
        ...coreLayers(context),
        ...customLayers(context),
        ...repoLayer(context),
        wrapLayer(METRICS_LAYER, renderMetrics(context)),
    ];
    return `${parts.join("\n\n")}\n`;
};
```
