# core/adapters/vcs.adapter.ts

> 48 lines of code and 10 definitions.

Tree: GovLab Stats
Language: typescript
Layer: infrastructure
Canonical: https://banes-lab.com/anatomy/stats#file-stats-core-adapters-vcs-adapter-ts
Source text: https://banes-lab.com/source/stats/core/adapters/vcs.adapter.ts.txt

Listed in [core/adapters](https://banes-lab.com/api/source/stats/core/adapters.md).

## Definitions

- `vcsQuery` (lexical_declaration, line 12)
- `parseShortlog` (lexical_declaration, line 17)
- `repoStats` (lexical_declaration, line 29)
- `collectGit` (lexical_declaration, line 46, exported)
- `result` (lexical_declaration, line 13)
- `trimmed` (lexical_declaration, line 19)
- `tab` (lexical_declaration, line 20)
- `count` (lexical_declaration, line 24)
- `tracked` (lexical_declaration, line 33)
- `repo` (lexical_declaration, line 47, exported)

## Contained in

- [core/adapters](https://banes-lab.com/anatomy/stats/folder-stats-core-adapters.md)

## Linked from

- [core/adapters](https://banes-lab.com/anatomy/stats/folder-stats-core-adapters.md)

## Source

```typescript
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) };
};
```
