core/adapters/vcs.adapter.ts

core/adapters/vcs.adapter.ts is a file in GovLab Stats. 48 lines of code and 10 definitions.

import type { Contributor, GitStats, RepoStats } from "#types/vcs.types";
import {
    INSIDE_WORK_TREE,
    SHORTLOG_SEPARATOR,
    VCS_BINARY,
    VCS_QUERIES,
    WORKSPACE_LABEL,
} from "#configuration/constants/vcs.constants";
import { LINE_BREAK } from "#configuration/constants/source.constants";
import { spawnSync } from "node:child_process";

const vcsQuery = function vcsQuery(cwd: string, args: readonly string[]): string | null {
    const result = spawnSync(VCS_BINARY, [...args], { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
    return result.status === 0 ? result.stdout.trim() : null;
};

const parseShortlog = function parseShortlog(text: string | null): Contributor[] {
    return (text ?? "").split(LINE_BREAK).flatMap((line): Contributor[] => {
        const trimmed = line.trim();
        const tab = trimmed.indexOf(SHORTLOG_SEPARATOR);
        if (tab === -1) {
            return [];
        }
        const count = Number(trimmed.slice(0, tab).trim());
        return [{ count: Number.isFinite(count) ? count : 0, who: trimmed.slice(tab + 1).trim() }];
    });
};

const repoStats = function repoStats(cwd: string): RepoStats | null {
    if (vcsQuery(cwd, VCS_QUERIES.inside) !== INSIDE_WORK_TREE) {
        return null;
    }
    const tracked = vcsQuery(cwd, VCS_QUERIES.tracked);
    return {
        branch: vcsQuery(cwd, VCS_QUERIES.branch),
        commits: Number(vcsQuery(cwd, VCS_QUERIES.commits) ?? "0"),
        firstDate: vcsQuery(cwd, VCS_QUERIES.firstDate),
        head: vcsQuery(cwd, VCS_QUERIES.head),
        label: WORKSPACE_LABEL,
        lastDate: vcsQuery(cwd, VCS_QUERIES.lastDate),
        shortlog: vcsQuery(cwd, VCS_QUERIES.shortlog),
        tracked: tracked === null ? 0 : tracked.split(LINE_BREAK).filter((line) => line.length > 0).length,
    };
};

export const collectGit = function collectGit(root: string): GitStats | null {
    const repo = repoStats(root);
    if (repo === null) {
        return null;
    }
    return { ...repo, contributors: parseShortlog(repo.shortlog).toSorted((a, b) => b.count - a.count) };
};