core/adapters/document.adapter.ts

core/adapters/document.adapter.ts is a file in Bane's Lab Build Scripts. 87 lines of code and 19 definitions.

import { JSDOM } from "jsdom";
import { setImmediate } from "node:timers";

const SLASH = "/";
const ROOT_SELECTOR = "html";

class IdleObserver {
    public observe(): void {}

    public unobserve(): void {}

    public disconnect(): void {}
}

const clearSelectorCache = function clearSelectorCache(document: Document): void {
    void document.querySelectorAll(ROOT_SELECTOR);
};

const release = function release(dom: JSDOM): void {
    const { document } = dom.window;
    document.documentElement.replaceChildren();
    clearSelectorCache(document);
    dom.window.close();
};

export const readDocument = function readDocument<T>(markup: string, read: (document: Document) => T): T {
    const dom = new JSDOM(markup);
    try {
        return read(dom.window.document);
    } finally {
        release(dom);
    }
};

export const rewriteDocument = function rewriteDocument(markup: string, edit: (document: Document) => void): string {
    const dom = new JSDOM(markup);
    try {
        edit(dom.window.document);
        return dom.serialize();
    } finally {
        release(dom);
    }
};

export const rewriteEach = function rewriteEach<T>(
    markup: string,
    items: readonly T[],
    edit: (document: Document, item: T) => void,
    write: (item: T, html: string) => void,
): void {
    const dom = new JSDOM(markup);
    try {
        for (const item of items) {
            edit(dom.window.document, item);
            write(item, dom.serialize());
        }
    } finally {
        release(dom);
    }
};

const settleTasks = async function settleTasks(): Promise<void> {
    await new Promise<void>((resolve) => {
        setImmediate(resolve);
    });
};

export const mapSettled = async function mapSettled<Item, Result>(
    items: readonly Item[],
    work: (item: Item) => Promise<Result> | Result,
): Promise<Result[]> {
    return items.reduce<Promise<Result[]>>(async (previous, item) => {
        const done = await previous;
        done.push(await work(item));
        await settleTasks();
        return done;
    }, Promise.resolve([]));
};

export const installDocument = function installDocument(site: string): (path: string) => void {
    const { window } = new JSDOM("", { url: site + SLASH });
    const globals: readonly (readonly [string, unknown])[] = [
        ["DOMParser", window.DOMParser],
        ["Element", window.Element],
        ["HTMLElement", window.HTMLElement],
        ["IntersectionObserver", IdleObserver],
        ["Node", window.Node],
        ["Text", window.Text],
        ["document", window.document],
        ["requestAnimationFrame", (callback: () => void): number => Number(setTimeout(callback, 0))],
        ["window", window],
    ];
    for (const [name, value] of globals) {
        Reflect.set(globalThis, name, value);
    }
    return (path) => {
        window.history.replaceState({}, "", path);
    };
};