add more clear types for getBranchInfo

268685ca2b2d95435aef7ae0cae6328464f07903

Tucker McKnight <tucker@pangolin.lan> | Mon Nov 03 2025

add more clear types for getBranchInfo
main.ts:15
Before
14
15
16
17
18
19
const ajv = new Ajv()
const exec = util.promisify(childProcess.exec)

export default async function repoViewer(eleventyConfig, reposConfiguration: ReposConfiguration) {
  const validator = ajv.compile(ConfigSchema)
  const valid = validator(reposConfiguration)
  if (!valid) {
After
14
15
16
17
18
19
const ajv = new Ajv()
const exec = util.promisify(childProcess.exec)

export default async function repoViewer(eleventyConfig: any, reposConfiguration: ReposConfiguration) {
  const validator = ajv.compile(ConfigSchema)
  const valid = validator(reposConfiguration)
  if (!valid) {
main.ts:34
Before
33
34
35
36
37
38
  const branchesData = branches(reposData)
  eleventyConfig.addGlobalData("branches", branchesData)

  eleventyConfig.addFilter("getFileName", (filePath) => {
    const pathParts = filePath.split("/")
    return pathParts[pathParts.length - 1]
  })
After
33
34
35
36
37
38
  const branchesData = branches(reposData)
  eleventyConfig.addGlobalData("branches", branchesData)

  eleventyConfig.addFilter("getFileName", (filePath: string) => {
    const pathParts = filePath.split("/")
    return pathParts[pathParts.length - 1]
  })
src/dataTypes.ts:1
Before
0
1
2
type Repository = {
  name: string,
  description?: string,
  cloneUrl: string,
After
0
1
2
export type Repository = {
  name: string,
  description?: string,
  cloneUrl: string,
src/helpers.ts:1
Before
0
1
2

3
import _ from 'lodash'
import * as Diff from 'diff'

⁣
type NavValues = {
  repoName: string | ((data: any) => string),
After
0
1
2
3
4
import _ from 'lodash'
import * as Diff from 'diff'
import {type Repository} from './dataTypes.ts'

type NavValues = {
  repoName: string | ((data: any) => string),
src/helpers.ts:14
Before
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
  afterText: string,
}

type DiffInfo = {
  file: string,
  lineNumber: number,
  previousText: string,
  afterText: string,
}

const getGitDiffsFromPatchText = (patchText: string): Array<DiffInfo> => {
  const lines = patchText.split("\n")
  const hunks: Array<Hunk> = []
  let previousHunk = -1
  const filenameRegex = RegExp(/diff --git a\/(.*?) b\/(.*?)/)
  const lineNumberRegex = RegExp(/@@ -(.*?)[,| ].*/)
After
13
14
15
16
17
18




19
20
21
22
23
  afterText: string,
}

type Diffs = ReturnType<Repository['commits']['get']>['diffs']


⁣
⁣
⁣
⁣
const getGitDiffsFromPatchText = (patchText: string): Diffs => {
  const lines = patchText.split("\n")
  const hunks: Diffs = []
  let previousHunk = -1
  const filenameRegex = RegExp(/diff --git a\/(.*?) b\/(.*?)/)
  const lineNumberRegex = RegExp(/@@ -(.*?)[,| ].*/)
src/helpers.ts:55
Before
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
      lastHunkBefore = _.escape(lastHunkBefore)
      lastHunkAfter = _.escape(lastHunkAfter)
      const changeObject = Diff.diffWordsWithSpace(lastHunkBefore, lastHunkAfter)
      let previousText = ""
      let afterText = ""

      changeObject.forEach((obj) => {
        if (!obj.added && !obj.removed) {
          previousText = previousText + obj.value
          afterText = afterText + obj.value
        }
        if (obj.added) {
          afterText = afterText + "<mark>" + obj.value + "</mark>"
        }
        if (obj.removed) {
          previousText = previousText + "<mark>" + obj.value + "</mark>"
        }
      })

      hunks.push({
        file: previousFilename !== '' ? previousFilename : currentFilename,
        lineNumber: parseInt(lines[previousHunk].match(lineNumberRegex)[1]),
        previousText,
        afterText,
      })
      previousFilename = ''
After
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
      lastHunkBefore = _.escape(lastHunkBefore)
      lastHunkAfter = _.escape(lastHunkAfter)
      const changeObject = Diff.diffWordsWithSpace(lastHunkBefore, lastHunkAfter)
      let beforeText = ""
      let afterText = ""

      changeObject.forEach((obj) => {
        if (!obj.added && !obj.removed) {
          beforeText = beforeText + obj.value
          afterText = afterText + obj.value
        }
        if (obj.added) {
          afterText = afterText + "<mark>" + obj.value + "</mark>"
        }
        if (obj.removed) {
          beforeText = beforeText + "<mark>" + obj.value + "</mark>"
        }
      })

      hunks.push({
        fileName: previousFilename !== '' ? previousFilename : currentFilename,
        lineNumber: parseInt(lines[previousHunk].match(lineNumberRegex)[1]),
        beforeText,
        afterText,
      })
      previousFilename = ''
src/repos.ts:4
Before
3
4
5

6
7
import {type BranchInfo} from './dataTypes.ts'

import repoOperations from './vcses/operations.ts'
⁣
import { getLocation} from './helpers.ts'
import repoHelpers from './vcses/helpers.ts'
After
3
4
5
6
7
8
import {type BranchInfo} from './dataTypes.ts'

import repoOperations from './vcses/operations.ts'
import { getBranchInfo } from './vcses/git/operations.ts'
import { getLocation} from './helpers.ts'
import repoHelpers from './vcses/helpers.ts'
src/repos.ts:35
Before
34
35
36
37
38
39
    const branchTuples: BranchInfoTuple[] = await Promise.all(branchNames.map(async (branchName): Promise<BranchInfoTuple> => {
      const repoLocation = getLocation(reposConfig, branchName, repoName)
      const files = await repoOperations[vcs].getFileList(repoName, branchName, repoLocation)
      const patches = await repoOperations[vcs].getBranchInfo(repoName, branchName, repoLocation)
      return [branchName, {
        files,
        patches,
After
34
35
36
37
38
39
    const branchTuples: BranchInfoTuple[] = await Promise.all(branchNames.map(async (branchName): Promise<BranchInfoTuple> => {
      const repoLocation = getLocation(reposConfig, branchName, repoName)
      const files = await repoOperations[vcs].getFileList(repoName, branchName, repoLocation)
      const patches = await getBranchInfo(branchName, repoLocation)
      return [branchName, {
        files,
        patches,
src/vcses/git/operations.ts:2
Before
1
2
3
4

5
import childProcess from 'child_process'
const exec = util.promisify(childProcess.exec)
import { getGitDiffsFromPatchText} from '../../helpers.ts'

⁣
export const getFileList = async (repoName: string, branchName: string, repoLocation: string) => {
  const command = `git ls-tree -r --name-only ${branchName}`
After
1
2
3
4
5
6
import childProcess from 'child_process'
const exec = util.promisify(childProcess.exec)
import { getGitDiffsFromPatchText} from '../../helpers.ts'
import { type Repository } from '../../dataTypes.ts'

export const getFileList = async (repoName: string, branchName: string, repoLocation: string) => {
  const command = `git ls-tree -r --name-only ${branchName}`
src/vcses/git/operations.ts:37
Before
36
37
38
39







40
41
42
  return Array.from(fileSet)
}

export const getBranchInfo = async (repoName: string, branchName: string, repoLocation: string) => {
⁣
⁣
⁣
⁣
⁣
⁣
⁣
  const patches = new Map()

  const totalPatchesCountRes = await exec(`(cd ${repoLocation} && git rev-list --count ${branchName})`)
  const totalPatchesCount = parseInt(totalPatchesCountRes.stdout)
After
36
37
38
39
40
41
42
43
44
45
46
47
48
49
  return Array.from(fileSet)
}

export const getBranchInfo = async (branchName: string, repoLocation: string) => {
  const patches: Map<string, {
    name: string,
    description: string,
    author: string,
    date: string,
    hash: string,
    diffs: ReturnType<Repository['commits']['get']>['diffs'],
  }> = new Map()

  const totalPatchesCountRes = await exec(`(cd ${repoLocation} && git rev-list --count ${branchName})`)
  const totalPatchesCount = parseInt(totalPatchesCountRes.stdout)
src/vcses/git/operations.ts:54
Before
53
54
55
56
57
58
      gitLogSubset = gitLogSubset.slice(nextPatchStart)

      const hash = currentPatch[0].replace("commit ", "").trim()
      let author, date
      [1, 2, 3].forEach((lineNumber) => {
        if (currentPatch[lineNumber].startsWith("Author")) {
          author = currentPatch[lineNumber].replace("Author: ", "").trim()
After
53
54
55
56
57
58
      gitLogSubset = gitLogSubset.slice(nextPatchStart)

      const hash = currentPatch[0].replace("commit ", "").trim()
      let author: string, date: string
      [1, 2, 3].forEach((lineNumber) => {
        if (currentPatch[lineNumber].startsWith("Author")) {
          author = currentPatch[lineNumber].replace("Author: ", "").trim()
src/vcses/operations.ts:1
Before
0
1
2
3
import {
  getBranchInfo as getGitBranchInfo,
  getFileList as getGitFileList,
  getFileLastTouchInfo as getGitFileLastTouchInfo,
} from './git/operations.ts'
After
0

1
2
import {
⁣
  getFileList as getGitFileList,
  getFileLastTouchInfo as getGitFileLastTouchInfo,
} from './git/operations.ts'
src/vcses/operations.ts:7
Before
6
7
8
9
10
11

type RepoOperationsType = {
  [vcs: string]: {
    getBranchInfo: (repoName: string, branchName: string, repoLocation: string) => Promise<BranchInfo['patches']>,
    getFileList: (repoName: string, branchName: string, repoLocation: string) => Promise<BranchInfo['files']>,
    getFileLastTouchInfo: (repoName: string, branchName: string, filename: string, repoLocation: string) => Promise<Array<{sha: string, author: string}>>,
  }
After
6
7
8

9
10

type RepoOperationsType = {
  [vcs: string]: {
⁣
    getFileList: (repoName: string, branchName: string, repoLocation: string) => Promise<BranchInfo['files']>,
    getFileLastTouchInfo: (repoName: string, branchName: string, filename: string, repoLocation: string) => Promise<Array<{sha: string, author: string}>>,
  }
src/vcses/operations.ts:15
Before
14
15
16
17
18
19

const repoOperations: RepoOperationsType = {
  git: {
    getBranchInfo: getGitBranchInfo,
    getFileList: getGitFileList,
    getFileLastTouchInfo: getGitFileLastTouchInfo,
  },
After
14
15
16

17
18

const repoOperations: RepoOperationsType = {
  git: {
⁣
    getFileList: getGitFileList,
    getFileLastTouchInfo: getGitFileLastTouchInfo,
  },
templates/patch.njk:27
Before
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
  {% set patchHunks = patchInfo.patch.diffs %}
  {% for hunk in patchHunks %}
    <div class=hunk>
      <span class="font-monospace fw-bold"><a href="{{reposPath}}/{{patchInfo.repoName | slugify}}/branches/{{patchInfo.branchName | slugify}}/files/{{ hunk.file | slugify}}.html">{{ hunk.file }}:{{ hunk.lineNumber }}</a></span>
      <div class="diff d-flex">
        <div class="flex-grow-1 diff-left pe-2">
          <span class='font-monospace text-secondary'>Before</span>
          <div class="row">
            <div class="col-auto border-end">
              <code>
{%- for lineNumber in hunk.previousText | lineNumbers -%}
{{ lineNumber + hunk.lineNumber - 1}}
{% endfor -%}
              </code>
            </div>
            <div class="col overflow-scroll">
              <pre data-start="{{hunk.lineNumber}}"><code data-type="before" class="line-numbers language-{{hunk.file | languageExtension(patchInfo.repoName)}}">{{hunk.previousText | safe}}</code></pre>
            </div>
          </div>
        </div>
After
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
  {% set patchHunks = patchInfo.patch.diffs %}
  {% for hunk in patchHunks %}
    <div class=hunk>
      <span class="font-monospace fw-bold"><a href="{{reposPath}}/{{patchInfo.repoName | slugify}}/branches/{{patchInfo.branchName | slugify}}/files/{{ hunk.fileName | slugify}}.html">{{ hunk.fileName }}:{{ hunk.lineNumber }}</a></span>
      <div class="diff d-flex">
        <div class="flex-grow-1 diff-left pe-2">
          <span class='font-monospace text-secondary'>Before</span>
          <div class="row">
            <div class="col-auto border-end">
              <code>
{%- for lineNumber in hunk.beforeText | lineNumbers -%}
{{ lineNumber + hunk.lineNumber - 1}}
{% endfor -%}
              </code>
            </div>
            <div class="col overflow-scroll">
              <pre data-start="{{hunk.lineNumber}}"><code data-type="before" class="line-numbers language-{{hunk.fileName | languageExtension(patchInfo.repoName)}}">{{hunk.beforeText | safe}}</code></pre>
            </div>
          </div>
        </div>
templates/patch.njk:55
Before
54
55
56
57
58
59
              </code>
            </div>
            <div class="col overflow-scroll">
              <pre data-start="{{hunk.lineNumber}}"><code data-type="after" class="line-numbers language-{{hunk.file | languageExtension(patchInfo.repoName)}}">{{ hunk.afterText | safe}}</code></pre>
            </div>
          </div>
        </div>
After
54
55
56
57
58
59
              </code>
            </div>
            <div class="col overflow-scroll">
              <pre data-start="{{hunk.lineNumber}}"><code data-type="after" class="line-numbers language-{{hunk.fileName | languageExtension(patchInfo.repoName)}}">{{ hunk.afterText | safe}}</code></pre>
            </div>
          </div>
        </div>