# core/converters/markdown.converter.ts

> 97 lines of code and 27 definitions.

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

Listed in [core/converters](https://banes-lab.com/api/source/docs/core/converters.md), after [core/converters/layer.converter.ts](https://banes-lab.com/source/docs/core/converters/layer.converter.ts.md) and before [core/converters/package.converter.ts](https://banes-lab.com/source/docs/core/converters/package.converter.ts.md).

## Definitions

- `blankMarkup` (lexical_declaration, line 83, exported)
- `wrapLinePaths` (lexical_declaration, line 87)
- `blankSpans` (lexical_declaration, line 72)
- `wrapLineKeepingReturn` (lexical_declaration, line 96)
- `backtickBarePaths` (lexical_declaration, line 103, exported)
- `TICK` (lexical_declaration, line 6)
- `CARRIAGE_RETURN` (lexical_declaration, line 7)
- `BACKTICK_DELIMS` (lexical_declaration, line 8)
- `LINK_DELIMS` (lexical_declaration, line 9)
- `runLength` (lexical_declaration, line 11)
- `findCloseEnd` (lexical_declaration, line 19)
- `closeRun` (lexical_declaration, line 26)
- `codeSpanAt` (lexical_declaration, line 35)
- `run` (lexical_declaration, line 36)
- `closeEnd` (lexical_declaration, line 37)
- `spanEnd` (lexical_declaration, line 38)
- `stripInlineCode` (lexical_declaration, line 42, exported)
- `nextBlankSpan` (lexical_declaration, line 58)
- `before` (lexical_declaration, line 63)
- `end` (lexical_declaration, line 64)
- `stop` (lexical_declaration, line 68)
- `out` (lexical_declaration, line 73, exported)
- `at` (lexical_declaration, line 74, exported)
- `span` (lexical_declaration, line 76, exported)
- `lines` (lexical_declaration, line 104, exported)
- `start` (lexical_declaration, line 105, exported)
- `mask` (lexical_declaration, line 106, exported)

## Contained in

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

## Uses

- [core/selectors/markdown.selector.ts](https://banes-lab.com/source/docs/core/selectors/markdown.selector.ts.md)

## Used by

- [core/analyzers/markdown.analyzer.ts](https://banes-lab.com/source/docs/core/analyzers/markdown.analyzer.ts.md)

## Linked from

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

## Source

```typescript
import { bodyStart, codeLineMask } from "#core/parsers/markdown.parser";
import type { SpanDelims } from "#types/reference.types";
import type { TextSpan } from "#types/markdown.types";
import { pathSpans } from "#core/selectors/markdown.selector";

const TICK = "`";
const CARRIAGE_RETURN = "\r";
const BACKTICK_DELIMS: SpanDelims = { close: TICK, open: TICK };
const LINK_DELIMS: SpanDelims = { close: ")", open: "](" };

const runLength = function runLength(line: string, from: number): number {
    let at = from;
    while (at < line.length && line.charAt(at) === TICK) {
        at += 1;
    }
    return at - from;
};

const findCloseEnd = function findCloseEnd(line: string, from: number, run: number): number {
    let at = from;
    while (at < line.length) {
        if (line.charAt(at) !== TICK) {
            at += 1;
            continue;
        }
        const closeRun = runLength(line, at);
        if (closeRun === run) {
            return at + closeRun;
        }
        at += closeRun;
    }
    return -1;
};

const codeSpanAt = function codeSpanAt(line: string, at: number): TextSpan {
    const run = runLength(line, at);
    const closeEnd = findCloseEnd(line, at + run, run);
    const spanEnd = closeEnd === -1 ? at + run : closeEnd;
    return { next: spanEnd, text: " ".repeat(spanEnd - at) };
};

export const stripInlineCode = function stripInlineCode(line: string): string {
    let out = "";
    let at = 0;
    while (at < line.length) {
        if (line.charAt(at) === TICK) {
            const span = codeSpanAt(line, at);
            out += span.text;
            at = span.next;
        } else {
            out += line.charAt(at);
            at += 1;
        }
    }
    return out;
};

const nextBlankSpan = function nextBlankSpan(line: string, delims: SpanDelims, from: number): TextSpan {
    const start = line.indexOf(delims.open, from);
    if (start === -1) {
        return { next: line.length, text: line.slice(from) };
    }
    const before = line.slice(from, start);
    const end = line.indexOf(delims.close, start + delims.open.length);
    if (end === -1) {
        return { next: line.length, text: before + " ".repeat(line.length - start) };
    }
    const stop = end + delims.close.length;
    return { next: stop, text: before + " ".repeat(stop - start) };
};

const blankSpans = function blankSpans(line: string, delims: SpanDelims): string {
    let out = "";
    let at = 0;
    while (at < line.length) {
        const span = nextBlankSpan(line, delims, at);
        out += span.text;
        at = span.next;
    }
    return out;
};

export const blankMarkup = function blankMarkup(line: string): string {
    return blankSpans(blankSpans(line, BACKTICK_DELIMS), LINK_DELIMS);
};

const wrapLinePaths = function wrapLinePaths(line: string): string {
    return pathSpans(blankMarkup(line))
        .toReversed()
        .reduce(
            (out, [start, end]) => `${out.slice(0, start)}${TICK}${out.slice(start, end)}${TICK}${out.slice(end)}`,
            line,
        );
};

const wrapLineKeepingReturn = function wrapLineKeepingReturn(line: string): string {
    if (!line.endsWith(CARRIAGE_RETURN)) {
        return wrapLinePaths(line);
    }
    return wrapLinePaths(line.slice(0, -1)) + CARRIAGE_RETURN;
};

export const backtickBarePaths = function backtickBarePaths(source: string): string {
    const lines = source.split("\n");
    const start = bodyStart(lines);
    const mask = codeLineMask(lines, start);
    return lines.map((line, at) => (at < start || mask[at] === true ? line : wrapLineKeepingReturn(line))).join("\n");
};
```
