# presentation/renderers/markdown.renderer.ts

> 110 lines of code and 17 definitions.

Tree: Site tree
Language: typescript
Layer: product
Canonical: https://banes-lab.com/anatomy/tree#file-presentation-renderers-markdown-renderer-ts
Source text: https://banes-lab.com/assets/sources/source.26d6900ff9715b13ed22e9f71774aa80611fcdabc124bc72583865e44b0cebbb.generated.txt

## Definitions

- `paragraph` (lexical_declaration, line 58)
- `table` (lexical_declaration, line 34)
- `list` (lexical_declaration, line 25)
- `heading` (lexical_declaration, line 20)
- `renderNode` (lexical_declaration, line 62)
- `renderDocument` (lexical_declaration, line 98, exported)
- `renderMarkdown` (lexical_declaration, line 92, exported)
- `HEADING_TAGS` (lexical_declaration, line 18)
- `tag` (lexical_declaration, line 21)
- `element` (lexical_declaration, line 26)
- `head` (lexical_declaration, line 35)
- `body` (lexical_declaration, line 40)
- `line` (lexical_declaration, line 44)
- `language` (lexical_declaration, line 68)
- `article` (lexical_declaration, line 93, exported)
- `holder` (lexical_declaration, line 102, exported)
- `rendered` (lexical_declaration, line 112, exported)

## Uses

- [core/assets/walk.assets.ts](https://banes-lab.com/source/tree/core/assets/walk.assets.ts.md)
- [core/converters/markdown.converter.ts](https://banes-lab.com/source/tree/core/converters/markdown.converter.ts.md)
- [core/factories/element.factory.ts](https://banes-lab.com/source/tree/core/factories/element.factory.ts.md)
- [core/loaders/walk.loader.ts](https://banes-lab.com/source/tree/core/loaders/walk.loader.ts.md)
- [core/normalizers/markdown.normalizer.ts](https://banes-lab.com/source/tree/core/normalizers/markdown.normalizer.ts.md)
- [core/predicates/document.predicate.ts](https://banes-lab.com/source/tree/core/predicates/document.predicate.ts.md)
- [presentation/components/code.component.ts](https://banes-lab.com/source/tree/presentation/components/code.component.ts.md)
- [presentation/components/source.component.ts](https://banes-lab.com/source/tree/presentation/components/source.component.ts.md)
- [presentation/renderers/source.renderer.ts](https://banes-lab.com/source/tree/presentation/renderers/source.renderer.ts.md)
- [presentation/renderers/text.renderer.ts](https://banes-lab.com/source/tree/presentation/renderers/text.renderer.ts.md)

## Source

```typescript
import { FALLBACK_LANGUAGE, SOURCE_LOADED_EVENT } from "#configuration/constants/anatomy.constants";
import type { MarkdownNode, MarkdownParagraph } from "#types/markdown.types";
import { SOURCE_LOADING, SOURCE_MISSING, SOURCE_SERVED_LEAD } from "#configuration/strings/walk.strings";
import { appendChildren, createElement } from "#core/factories/element.factory";
import { enableSourceNavigation, markDocumentReferences } from "#presentation/components/source.component";
import type { ElementTag } from "#types/element.types";
import { MARKDOWN_CLASS } from "#configuration/constants/markdown.constants";
import type { MarkdownBlock } from "#types/block.types";
import { createPlainCodeBlock } from "#presentation/components/code.component";
import { fillMarkup } from "#presentation/renderers/text.renderer";
import { inlineMarkup } from "#core/normalizers/markdown.normalizer";
import { isStaticRender } from "#core/predicates/document.predicate";
import { loadSource } from "#core/loaders/walk.loader";
import { parseMarkdown } from "#core/converters/markdown.converter";
import { renderServedSource } from "#presentation/renderers/source.renderer";
import { sourceLocation } from "#assets/walk.assets";

const HEADING_TAGS: readonly ElementTag[] = ["h1", "h2", "h3", "h4", "h5", "h6"];

const heading = function heading(level: number, text: string): Element {
    const tag = HEADING_TAGS[level - 1] ?? "h6";
    return fillMarkup(createElement(tag, { className: "markdown-heading" }), inlineMarkup(text));
};

const list = function list(ordered: boolean, items: readonly string[]): Element {
    const element = createElement(ordered ? "ol" : "ul", { className: "markdown-list" });
    appendChildren(
        element,
        items.map((item) => fillMarkup(createElement("li"), inlineMarkup(item))),
    );
    return element;
};

const table = function table(headers: readonly string[], rows: readonly (readonly string[])[]): Element {
    const head = createElement("tr");
    appendChildren(
        head,
        headers.map((cell) => fillMarkup(createElement("th"), inlineMarkup(cell))),
    );
    const body = createElement("tbody");
    appendChildren(
        body,
        rows.map((row) => {
            const line = createElement("tr");
            appendChildren(
                line,
                row.map((cell) => fillMarkup(createElement("td"), inlineMarkup(cell))),
            );
            return line;
        }),
    );
    return createElement("table", {
        children: [createElement("thead", { children: [head] }), body],
        className: "markdown-table",
    });
};

const paragraph = function paragraph(node: MarkdownParagraph): Element {
    return fillMarkup(createElement("p", { className: "markdown-text" }), inlineMarkup(node.text));
};

const renderNode = function renderNode(node: MarkdownNode): Element {
    switch (node.kind) {
        case "heading": {
            return heading(node.level, node.text);
        }
        case "code": {
            const language = node.language.length === 0 ? FALLBACK_LANGUAGE : node.language;
            return createPlainCodeBlock(node.code, language, language);
        }
        case "list": {
            return list(node.ordered, node.items);
        }
        case "quote": {
            return fillMarkup(createElement("blockquote", { className: "markdown-quote" }), inlineMarkup(node.text));
        }
        case "rule": {
            return createElement("hr", { className: "markdown-rule" });
        }
        case "table": {
            return table(node.headers, node.rows);
        }
        case "paragraph": {
            return paragraph(node);
        }
        default: {
            return paragraph(node);
        }
    }
};

export const renderMarkdown = function renderMarkdown(text: string): Element {
    const article = createElement("article", { className: MARKDOWN_CLASS });
    appendChildren(article, parseMarkdown(text).map(renderNode));
    return article;
};

export const renderDocument = function renderDocument(block: MarkdownBlock): Element {
    if (isStaticRender()) {
        return renderServedSource(SOURCE_SERVED_LEAD, sourceLocation(block.source), block.title);
    }
    const holder = createElement("div", {
        children: [createPlainCodeBlock(SOURCE_LOADING, block.title, FALLBACK_LANGUAGE)],
        className: "document-holder",
    });
    enableSourceNavigation(holder);
    void loadSource(block.source).then((text) => {
        if (text === null) {
            holder.replaceChildren(createPlainCodeBlock(SOURCE_MISSING, block.title, FALLBACK_LANGUAGE));
            return;
        }
        const rendered = renderMarkdown(text);
        markDocumentReferences(rendered, block.path);
        holder.replaceChildren(createElement("div", { className: "document-title", text: block.title }), rendered);
        holder.dispatchEvent(new Event(SOURCE_LOADED_EVENT, { bubbles: true }));
    });
    return holder;
};
```
