tools/core/analyzers/graph.analyzer.ts

tools/core/analyzers/graph.analyzer.ts is a file in Coordination Surface. 99 lines of code and 29 definitions.

import { dirname, resolve } from "node:path";
import { isFilesystemRefusal } from "../predicates/file.predicate.ts";

const CYCLE_JOIN = ">";
const IMPORT_LEAD = "import ";
const TYPE_IMPORT_LEAD = "import type ";
const FROM_MARK = 'from "';
const QUOTE = '"';
const RELATIVE_LEADS = ["./", "../"];

export const cyclesIn = function cyclesIn(edges: ReadonlyMap<string, ReadonlySet<string>>): string[][] {
    const out: string[][] = [];
    const reported = new Set<string>();

    const walk = (node: string, trail: readonly string[]): void => {
        const at = trail.indexOf(node);
        if (at !== -1) {
            const cycle = trail.slice(at);
            const marker = cycle.toSorted((left, right) => left.localeCompare(right)).join(CYCLE_JOIN);
            if (!reported.has(marker)) {
                reported.add(marker);
                out.push([...cycle]);
            }
            return;
        }
        if (trail.length > edges.size) {
            return;
        }
        for (const next of edges.get(node) ?? []) {
            walk(next, [...trail, node]);
        }
    };

    for (const node of edges.keys()) {
        walk(node, []);
    }
    return out;
};

const specifierIn = function specifierIn(line: string): string | null {
    const from = line.indexOf(FROM_MARK);
    if (from === -1) {
        return null;
    }
    const start = from + FROM_MARK.length;
    const end = line.indexOf(QUOTE, start);
    return end === -1 ? "" : line.slice(start, end);
};

const isRelative = function isRelative(specifier: string): boolean {
    return RELATIVE_LEADS.some((lead) => specifier.startsWith(lead));
};

export const localImports = function localImports(path: string, source: string, known: ReadonlySet<string>): string[] {
    const out = new Set<string>();
    const directory = dirname(path);
    let inside = false;

    for (const line of source.split("\n")) {
        inside ||= line.trim().startsWith(IMPORT_LEAD);
        const specifier = inside ? specifierIn(line) : null;
        if (specifier === null) {
            continue;
        }
        inside = false;
        const posix = isRelative(specifier) ? resolve(directory, specifier).replaceAll("\\", "/") : "";
        for (const candidate of known) {
            if (posix.length > 0 && posix.endsWith(candidate)) {
                out.add(candidate);
            }
        }
    }

    return [...out];
};

const withoutTypeImports = function withoutTypeImports(source: string): string {
    const out: string[] = [];
    let skipping = false;

    for (const line of source.split("\n")) {
        const trimmed = line.trim();
        skipping ||= trimmed.startsWith(TYPE_IMPORT_LEAD);
        if (!skipping) {
            out.push(line);
        }
        if (skipping && trimmed.includes(FROM_MARK)) {
            skipping = false;
        }
    }

    return out.join("\n");
};

export const moduleSource = function moduleSource(
    path: string,
    read: (path: string) => string,
    known: ReadonlySet<string>,
): string {
    const source = read(path);
    let combined = source;

    for (const imported of localImports(path, withoutTypeImports(source), known)) {
        try {
            combined += `\n${read(imported)}`;
        } catch (error) {
            if (!isFilesystemRefusal(error)) {
                throw error;
            }
        }
    }

    return combined;
};