core/parsers/binding.parser.ts

core/parsers/binding.parser.ts is a file in GovLab Docs. 64 lines of code and 20 definitions.

import type { BracedSpan, SlotRef } from "#types/reference.types";
import { splitLines } from "#core/parsers/markdown.parser";

const OPEN = "{";
const CLOSE = "}";
const TICK = "`";
const SEGMENT = ".";
const SLOT_CHARS: ReadonlySet<string> = new Set("abcdefghijklmnopqrstuvwxyz0123456789_-.");

const isSlotName = function isSlotName(inner: string): boolean {
    if (inner.length === 0) {
        return false;
    }
    for (const char of inner) {
        if (!SLOT_CHARS.has(char)) {
            return false;
        }
    }
    return true;
};

const rootOf = function rootOf(slot: string): string {
    const dot = slot.indexOf(SEGMENT);
    return dot === -1 ? slot : slot.slice(0, dot);
};

const bracesIn = function bracesIn(line: string): BracedSpan[] {
    const found: BracedSpan[] = [];
    let from = 0;
    while (from < line.length) {
        const open = line.indexOf(OPEN, from);
        const close = open === -1 ? -1 : line.indexOf(CLOSE, open + 1);
        if (close === -1) {
            return found;
        }
        found.push({ col: open + 1, inner: line.slice(open + 1, close) });
        from = open + 1;
    }
    return found;
};

const isTicked = function isTicked(line: string, span: BracedSpan): boolean {
    return line.charAt(span.col - 2) === TICK && line.charAt(span.col + span.inner.length + 1) === TICK;
};

export const declaredSlotsOf = function declaredSlotsOf(adapterSource: string): ReadonlySet<string> {
    const declared = new Set<string>();
    for (const line of splitLines(adapterSource)) {
        for (const span of bracesIn(line)) {
            if (isTicked(line, span) && isSlotName(span.inner)) {
                declared.add(span.inner);
            }
        }
    }
    return declared;
};

export const slotFamiliesOf = function slotFamiliesOf(declared: ReadonlySet<string>): ReadonlySet<string> {
    return new Set([...declared].map(rootOf));
};

const isFamilySlot = function isFamilySlot(inner: string, families: ReadonlySet<string>): boolean {
    return isSlotName(inner) && families.has(rootOf(inner));
};

const slotsInLine = function slotsInLine(line: string, lineNo: number, families: ReadonlySet<string>): SlotRef[] {
    return bracesIn(line).flatMap((span) =>
        isFamilySlot(span.inner, families) ? [{ col: span.col, line: lineNo, slot: span.inner }] : [],
    );
};

export const slotRefs = function slotRefs(source: string, families: ReadonlySet<string>): SlotRef[] {
    return splitLines(source).flatMap((line, index) => slotsInLine(line, index + 1, families));
};