# core/formatters/markdown.formatter.ts

> 80 lines of code and 34 definitions.

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

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

## Definitions

- `renderRenderable` (lexical_declaration, line 40, exported)
- `titleCase` (lexical_declaration, line 27, exported)
- `bullets` (lexical_declaration, line 23, exported)
- `renderQuickStart` (lexical_declaration, line 55, exported)
- `renderConfiguration` (lexical_declaration, line 73, exported)
- `optionSpans` (lexical_declaration, line 59)
- `renderFieldFragment` (lexical_declaration, line 77, exported)
- `quickStartBlock` (lexical_declaration, line 50)
- `configurationLine` (lexical_declaration, line 65)
- `WORD_JOIN` (lexical_declaration, line 5)
- `DEFAULT_LANG` (lexical_declaration, line 6)
- `FENCE` (lexical_declaration, line 7)
- `RECORD_SEPARATOR` (lexical_declaration, line 8)
- `DEFAULT_KEY` (lexical_declaration, line 9)
- `NOTE_KEY` (lexical_declaration, line 10)
- `LANG_KEY` (lexical_declaration, line 11)
- `INTENT_KEY` (lexical_declaration, line 12)
- `CODE_KEY` (lexical_declaration, line 13)
- `OPTION_KEY` (lexical_declaration, line 14)
- `QUICK_START_FIELD` (lexical_declaration, line 15)
- `CONFIGURATION_FIELD` (lexical_declaration, line 16)
- `API_NOTES_FIELD` (lexical_declaration, line 17)
- `NOTE_JOIN` (lexical_declaration, line 18)
- `OPTION_SEPARATOR` (lexical_declaration, line 19)
- `LIST_JOIN` (lexical_declaration, line 20)
- `LAST_JOIN` (lexical_declaration, line 21)
- `recordLines` (lexical_declaration, line 34)
- `lang` (lexical_declaration, line 51)
- `spans` (lexical_declaration, line 60)
- `last` (lexical_declaration, line 61)
- `fallback` (lexical_declaration, line 66)
- `defaulted` (lexical_declaration, line 67)
- `note` (lexical_declaration, line 68)
- `noted` (lexical_declaration, line 69)

## 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/predicates/record.predicate.ts](https://banes-lab.com/source/docs/core/predicates/record.predicate.ts.md)

## Used by

- [core/converters/system.converter.ts](https://banes-lab.com/source/docs/core/converters/system.converter.ts.md)
- [core/formatters/catalog.formatter.ts](https://banes-lab.com/source/docs/core/formatters/catalog.formatter.ts.md)
- [core/formatters/readme.formatter.ts](https://banes-lab.com/source/docs/core/formatters/readme.formatter.ts.md)
- [core/formatters/readme.section.formatter.ts](https://banes-lab.com/source/docs/core/formatters/readme.section.formatter.ts.md)
- [core/formatters/template.formatter.ts](https://banes-lab.com/source/docs/core/formatters/template.formatter.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/formatters](https://banes-lab.com/anatomy/docs/folder-docs-core-formatters.md)
- [core/predicates](https://banes-lab.com/anatomy/docs/folder-docs-core-predicates.md)

## Source

````typescript
import { exampleLabel } from "#configuration/strings/readme.strings";
import { isApiNote } from "#core/predicates/readme.predicate";
import { isRecordArray } from "#core/predicates/record.predicate";

const WORD_JOIN = "-";
const DEFAULT_LANG = "js";
const FENCE = "```";
const RECORD_SEPARATOR = "\n\n";
const DEFAULT_KEY = "default";
const NOTE_KEY = "note";
const LANG_KEY = "lang";
const INTENT_KEY = "intent";
const CODE_KEY = "code";
const OPTION_KEY = "option";
const QUICK_START_FIELD = "quickStart";
const CONFIGURATION_FIELD = "configuration";
const API_NOTES_FIELD = "apiNotes";
const NOTE_JOIN = " — ";
const OPTION_SEPARATOR = ", ";
const LIST_JOIN = ", ";
const LAST_JOIN = " and ";

export const bullets = function bullets(items: readonly string[]): string {
    return items.map((item) => `- ${item}`).join("\n");
};

export const titleCase = function titleCase(id: string): string {
    return id
        .split(WORD_JOIN)
        .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
        .join(" ");
};

const recordLines = function recordLines(record: Readonly<Record<string, string>>): string {
    return Object.entries(record)
        .map(([key, value]) => `- **${key}**: ${value}`)
        .join("\n");
};

export const renderRenderable = function renderRenderable(value: unknown): string {
    if (typeof value === "string") {
        return value;
    }
    if (Array.isArray(value) && value.every((entry) => typeof entry === "string")) {
        return bullets(value);
    }
    return isRecordArray(value) ? value.map(recordLines).join(RECORD_SEPARATOR) : "";
};

const quickStartBlock = function quickStartBlock(entry: Readonly<Record<string, string>>): string {
    const lang = entry[LANG_KEY] ?? DEFAULT_LANG;
    return `${FENCE}${lang}${exampleLabel(entry[INTENT_KEY] ?? "")}\n${entry[CODE_KEY] ?? ""}\n${FENCE}`;
};

export const renderQuickStart = function renderQuickStart(value: unknown): string {
    return isRecordArray(value) ? value.map(quickStartBlock).join(RECORD_SEPARATOR) : renderRenderable(value);
};

const optionSpans = function optionSpans(option: string): string {
    const spans = option.split(OPTION_SEPARATOR).map((key) => `\`${key}\``);
    const last = spans.pop() ?? "";
    return spans.length === 0 ? last : `${spans.join(LIST_JOIN)}${LAST_JOIN}${last}`;
};

const configurationLine = function configurationLine(entry: Readonly<Record<string, string>>): string {
    const fallback = entry[DEFAULT_KEY];
    const defaulted = fallback === undefined ? "" : ` (default: \`${fallback}\`)`;
    const note = entry[NOTE_KEY] ?? "";
    const noted = note === "" ? "" : NOTE_JOIN + note;
    return `- ${optionSpans(entry[OPTION_KEY] ?? "")}${defaulted}${noted}`;
};

export const renderConfiguration = function renderConfiguration(value: unknown): string {
    return isRecordArray(value) ? value.map(configurationLine).join("\n") : renderRenderable(value);
};

export const renderFieldFragment = function renderFieldFragment(key: string, value: unknown): string {
    if (key === QUICK_START_FIELD) {
        return renderQuickStart(value);
    }
    if (key === CONFIGURATION_FIELD) {
        return renderConfiguration(value);
    }
    if (key === API_NOTES_FIELD) {
        return (Array.isArray(value) ? value : [])
            .filter(isApiNote)
            .map((note) => note.note)
            .join("\n");
    }
    return renderRenderable(value);
};
````
