core/normalizers/text.normalizer.ts

core/normalizers/text.normalizer.ts is a file in Bane's Lab Site. 82 lines of code and 27 definitions.

import { LINE_BREAK } from "#configuration/constants/code.constants";
import { SPACE } from "#configuration/constants/document.constants";

import type { TextPart } from "#types/markup.types";

const WHITESPACE = new Set([SPACE, LINE_BREAK, "\t", "\r"]);
const MAX_BREAKS = 2;
const FENCE_MARK = "```";
const ASCII_LIMIT = 128;

const isLetter = function isLetter(char: string): boolean {
    const lower = char.toLowerCase();
    return lower >= "a" && lower <= "z";
};

const isWordChar = function isWordChar(char: string): boolean {
    const code = char.codePointAt(0) ?? 0;
    return isLetter(char) || (char >= "0" && char <= "9") || code >= ASCII_LIMIT;
};

const isSpaced = function isSpaced(char: string): boolean {
    return WHITESPACE.has(char);
};

const needsSpace = function needsSpace(last: string, next: string, elements: boolean): boolean {
    if (isSpaced(last) || isSpaced(next)) {
        return false;
    }
    return elements || (isWordChar(last) && isWordChar(next));
};

export const joinParts = function joinParts(parts: readonly TextPart[]): string {
    const pieces: string[] = [];
    let last = SPACE;
    let previous = false;
    for (const part of parts.filter((entry) => entry.text.length > 0)) {
        const next = part.text.at(0) ?? SPACE;
        if (needsSpace(last, next, previous && part.element)) {
            pieces.push(SPACE);
        }
        pieces.push(part.text);
        last = part.text.at(-1) ?? last;
        previous = part.element;
    }
    return pieces.join("");
};

export const collapseSpaces = function collapseSpaces(text: string): string {
    let result = "";
    let pending = false;
    for (const character of text) {
        if (WHITESPACE.has(character)) {
            pending = true;
            continue;
        }
        result += (pending ? SPACE : "") + character;
        pending = false;
    }
    return pending ? result + SPACE : result;
};

const trimSpaces = function trimSpaces(line: string): string {
    let start = 0;
    let end = line.length;
    while (start < end && line.charAt(start) === SPACE) {
        start += 1;
    }
    while (end > start && line.charAt(end - 1) === SPACE) {
        end -= 1;
    }
    return line.slice(start, end);
};

export const collapseBreaks = function collapseBreaks(text: string): string {
    const lines: string[] = [];
    let fenced = false;
    let blanks = 0;
    for (const line of text.split(LINE_BREAK)) {
        const marks = trimSpaces(line).startsWith(FENCE_MARK);
        if (fenced && !marks) {
            lines.push(line);
            continue;
        }
        fenced = marks ? !fenced : fenced;
        const kept = trimSpaces(line);
        blanks = kept.length === 0 ? blanks + 1 : 0;
        if (blanks < MAX_BREAKS) {
            lines.push(kept);
        }
    }
    return lines.join(LINE_BREAK).trim();
};