Tucker McKnight <tucker@pangolin.lan> | Sun Mar 01 2026
Change references of "branches" to "rels", allow for tags, too "rels" refers to a branch _or_ a tag. Flattened data lists (flatPatches, flatFiles, and branches.ts (now flatRels.ts)) should specify which type of rel each of their entries is. Pages now have the rel type (branch or tag) in their permalink instead of always being hard-coded to /branches/.
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
data: any,
) => {
const slugify = eleventyConfig.getFilter("slugify")
const nav = NavHelper(reposConfig, slugify, data.branchInfo.repoName, data.branchInfo.branchName)
const branchesWithWorkInProgress = data.branches.filter(branch => branch.ahead > 0)
const branchesFullyMerged = data.branches.filter(branch => branch.ahead === 0)
const branchCards = (branches) => {
return m('ul', branches.map((branch) => {
return branch.repoName === data.branchInfo.repoName ?
m('li', [
m('a', {
href: `${data.reposPath}/${slugify(branch.repoName)}/branches/${slugify(branch.branchName)}/branches`
}, branch.branchName),
branch.branchName === data.branchInfo.branchName
? m('div', {class: "badge rounded-pill bg-secondary mx-1"}, 'current') : null,
branch.branchName === data.reposConfig.repos[branch.repoName].defaultBranch
? m('div', {class: "badge rounded-pill bg-info text-dark mx-1"}, 'default') : null,
m('ul', [
m('li', [7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
data: any,
) => {
const slugify = eleventyConfig.getFilter("slugify")
const nav = NavHelper(reposConfig, slugify, data.flatRel.repoName, data.flatRel.relName)
const branchesWithWorkInProgress = data.flatRels.filter(rel => rel.type === "branch" && rel.ahead > 0)
const branchesFullyMerged = data.flatRels.filter(rel => rel.type === "branch" && rel.ahead === 0)
const branchCards = (branches) => {
return m('ul', branches.map((branch) => {
return branch.repoName === data.flatRel.repoName ?
m('li', [
m('a', {
href: `${data.reposPath}/${slugify(branch.repoName)}/branches/${slugify(branch.relName)}/branches`
}, branch.relName),
branch.relName === data.flatRel.relName
? m('div', {class: "badge rounded-pill bg-secondary mx-1"}, 'current') : null,
branch.relName === data.reposConfig.repos[branch.repoName].defaultBranch
? m('div', {class: "badge rounded-pill bg-info text-dark mx-1"}, 'default') : null,
m('ul', [
m('li', [6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
eleventyConfig: any,
data: any,
) => {
const pagesJustForBranch = eleventyConfig.getFilter("pagesJustForBranch")
const slugify = eleventyConfig.getFilter("slugify")
const date = eleventyConfig.getFilter("date")
const nav = NavHelper(reposConfig, slugify, data.patchPage.repoName, data.patchPage.branchName)
const pageContent = [
m('div', {class: "row mt-3 mb-1"},
m('div', {class: "col"},
m('p', {class: "d-inline-block"}, [
'Showing commits from ',
m('span', {class: "font-monospace me-2"}, data.patchPage.branchName),
m('a', {
class: "btn btn-outline-primary badge shadow-none",
href: nav.repoCurrentBranchRssFeed()6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
eleventyConfig: any,
data: any,
) => {
const pagesJustForRel = eleventyConfig.getFilter("pagesJustForRel")
const slugify = eleventyConfig.getFilter("slugify")
const date = eleventyConfig.getFilter("date")
const nav = NavHelper(reposConfig, slugify, data.patchPage.repoName, data.patchPage.relName)
const pageContent = [
m('div', {class: "row mt-3 mb-1"},
m('div', {class: "col"},
m('p', {class: "d-inline-block"}, [
'Showing commits from ',
m('span', {class: "font-monospace me-2"}, data.patchPage.relName),
m('a', {
class: "btn btn-outline-primary badge shadow-none",
href: nav.repoCurrentBranchRssFeed()33 34 35 36 37 38 39 40 41 42 43 44 45 46
),
m('nav',
m('ul', {class: "pagination"},
pagesJustForBranch(
data.paginatedPatches,
data.patchPage.repoName,
data.patchPage.branchName
).map((pageObj) => {
return m('li', {class: "page-item"},
m('a', {
class: `page-link ${pageObj.pageNumber === data.patchPage.pageNumber ? 'active' : ''}`,
href: `${data.reposPath}/${slugify(data.patchPage.repoName)}/branches/${data.patchPage.branchName}/commits/page${pageObj.pageNumber}`
}, pageObj.pageNumber)
)
})33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
),
m('nav',
m('ul', {class: "pagination"},
pagesJustForRel(
data.paginatedPatches,
data.patchPage.repoName,
data.patchPage.relName,
data.patchPage.type,
).map((pageObj) => {
return m('li', {class: "page-item"},
m('a', {
class: `page-link ${pageObj.pageNumber === data.patchPage.pageNumber ? 'active' : ''}`,
href: `${data.reposPath}/${slugify(data.patchPage.repoName)}/${data.patchPage.type}/${data.patchPage.relName}/commits/page${pageObj.pageNumber}`
}, pageObj.pageNumber)
)
})55 56 57 58 59 60
commit.isMerge ? m('span', {class: 'badge rounded-pill bg-primary me-1'}, 'merge') : null,
m('a', {
class: "fs-5",
href: `${data.reposPath}/${slugify(data.patchPage.repoName)}/branches/${slugify(data.patchPage.branchName)}/commits/${commit.hash}`,
}, commit.message.split('\n').slice(0, 1)),
m('br'),
m('span', date(commit.date)),55 56 57 58 59 60
commit.isMerge ? m('span', {class: 'badge rounded-pill bg-primary me-1'}, 'merge') : null,
m('a', {
class: "fs-5",
href: `${data.reposPath}/${slugify(data.patchPage.repoName)}/${data.patchPage.type}/${slugify(data.patchPage.relName)}/commits/${commit.hash}`,
}, commit.message.split('\n').slice(0, 1)),
m('br'),
m('span', date(commit.date)),66 67 68 69 70 71 72 73 74 75 76 77 78 79
})),
m('nav',
m('ul', {class: "pagination"},
pagesJustForBranch(
data.paginatedPatches,
data.patchPage.repoName,
data.patchPage.branchName
).map((pageObj) => {
return m('li', {class: "page-item"},
m('a', {
class: `page-link ${pageObj.pageNumber === data.patchPage.pageNumber ? 'active' : ''}`,
href: `${data.reposPath}/${slugify(data.patchPage.repoName)}/branches/${data.patchPage.branchName}/commits/page${pageObj.pageNumber}`
}, pageObj.pageNumber)
)
})66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
})),
m('nav',
m('ul', {class: "pagination"},
pagesJustForRel(
data.paginatedPatches,
data.patchPage.repoName,
data.patchPage.relName,
data.patchPage.type,
).map((pageObj) => {
return m('li', {class: "page-item"},
m('a', {
class: `page-link ${pageObj.pageNumber === data.patchPage.pageNumber ? 'active' : ''}`,
href: `${data.reposPath}/${slugify(data.patchPage.repoName)}/${data.patchPage.type}/${data.patchPage.relName}/commits/page${pageObj.pageNumber}`
}, pageObj.pageNumber)
)
})10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
}
const repo: Repository = data.currentRepo
const branch: Repository['branches'][0] = data.currentBranch
const slugify = eleventyConfig.getFilter("slugify")
const nav = NavHelper(reposConfig, slugify, repo.name, branch.name)
const branchesWithHrefs = repo.branches.map((branch) => {
return {
name: branch.name,
href: nav.repoBranchHome(branch.name) + '/' + data.nav.path,
date: repo.commits.get(branch.head).date.toISOString(),
}
}).concat(repo.tags.map((tag) => {
return {10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
}
const repo: Repository = data.currentRepo
const rel: Repository['branches'][0] = data.currentRel
const slugify = eleventyConfig.getFilter("slugify")
const nav = NavHelper(reposConfig, slugify, repo.name, rel.name)
const branchesWithHrefs = repo.branches.map((branch) => {
return {
name: branch.name,
href: nav.repoBranchHome(branch.name) + '/' + data.nav.path,
date: repo.commits.get(branch.sha).date.toISOString(),
}
}).concat(repo.tags.map((tag) => {
return {38 39 40 41 42 43
m('script', m.trust(`
window.branchesWithHrefs = ${JSON.stringify(branchesWithHrefs)};
window.defaultBranch = "${repo.defaultBranch}";
window.currentBranch = "${branch.name}";
window.cloneUrl = "${repo.cloneUrl}";
`)),
m('link', {38 39 40 41 42 43
m('script', m.trust(`
window.branchesWithHrefs = ${JSON.stringify(branchesWithHrefs)};
window.defaultBranch = "${repo.defaultBranch}";
window.currentBranch = "${rel.name}";
window.cloneUrl = "${repo.cloneUrl}";
`)),
m('link', {70 71 72 73 74 75
'data-bs-toggle': "dropdown",
'data-bs-auto-close': "outside",
'aria-expanded': "false"
}, branch.name),
m('div', {class: "dropdown-menu"}, [
m('form', {class: "mx-3 my-1"}, [
m('input', {type: "text", class: "form-control", id: "dropdownBranchSearch", placeholder: "Search branches..."}),70 71 72 73 74 75
'data-bs-toggle': "dropdown",
'data-bs-auto-close': "outside",
'aria-expanded': "false"
}, rel.name),
m('div', {class: "dropdown-menu"}, [
m('form', {class: "mx-3 my-1"}, [
m('input', {type: "text", class: "form-control", id: "dropdownBranchSearch", placeholder: "Search branches..."}),94 95 96 97 98 99
]),
m('div', {class: "dropdown-divider"}),
m('div', {id: "dropdown-branches-results", class: "dropdown-branches"},
branchesListItems(branchesWithHrefs, repo.defaultBranch, branch.name, 'date')
)
])
])94 95 96 97 98 99
]),
m('div', {class: "dropdown-divider"}),
m('div', {id: "dropdown-branches-results", class: "dropdown-branches"},
branchesListItems(branchesWithHrefs, repo.defaultBranch, rel.name, 'date')
)
])
])0 1 2 3 4 5
import m from 'mithril'
import render from 'mithril-node-render'
import { type Repository } from '../src/dataTypes.ts'
import branches from '../src/branches.ts'
import { dateToRfc3339 } from '@11ty/eleventy-plugin-rss'
import flatPatches from '../src/flatPatches.ts'
0 1 2 3 4 5
import m from 'mithril'
import render from 'mithril-node-render'
import { type Repository } from '../src/dataTypes.ts'
import getFlatRels from '../src/flatRels.ts'
import { dateToRfc3339 } from '@11ty/eleventy-plugin-rss'
import flatPatches from '../src/flatPatches.ts'
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
eleventyConfig: any,
) => {
return (data) => {
const branch: ReturnType<typeof branches>[0] = data.branch
const currentRepo: Repository = data.currentRepo
const currentBranch: Repository['branches'][0] = data.currentBranch
const currentBranchCommits: Awaited<ReturnType<typeof flatPatches>> = data.currentBranchCommits
const slugify = eleventyConfig.getFilter("slugify")
return render([
m.trust('<?xml version="1.0" encoding="utf-8"?>'),
m('feed', {xmlns: "http://www.w3.org/2005/Atom"}, [
m('title', `Latest patches in ${branch.branchName}`),
m.trust(`<link href="${ data.reposConfig.baseUrl + '/repos/' + slugify(branch.repoName) + '/branches/' + slugify(branch.branchName) + '/commits.xml'}" rel="self" />`),
m.trust(`<link href="${data.reposConfig.baseUrl + '/repos/' + slugify(branch.repoName) + '/branches/' + slugify(branch.branchName)}" />`),
m('updated', dateToRfc3339(currentRepo.commits.get(currentBranch.head).date)),
m('id', data.reposConfig.baseUrl),
m('author',
m('name', `${currentRepo.name} contributors`)
),
currentBranchCommits.map((commit) => {
const commitUrl = data.reposConfig.baseUrl + '/repos/' + slugify(branch.repoName) + '/branches/' + slugify(branch.branchName) + '/commits/' + commit.commit.hash
return m('entry', [
m('title', commit.commit.message.split('\n')[0]),
m('author', m('name', commit.commit.author)),8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
eleventyConfig: any,
) => {
return (data) => {
const flatRel: ReturnType<typeof getFlatRels>[0] = data.flatRel
const currentRepo: Repository = data.currentRepo
const currentRel: Repository['branches'][0] = data.currentRel
const currentRelCommits: Awaited<ReturnType<typeof flatPatches>> = data.currentRelCommits
const slugify = eleventyConfig.getFilter("slugify")
return render([
m.trust('<?xml version="1.0" encoding="utf-8"?>'),
m('feed', {xmlns: "http://www.w3.org/2005/Atom"}, [
m('title', `Latest patches in ${flatRel.relName}`),
m.trust(`<link href="${ data.reposConfig.baseUrl + '/repos/' + slugify(flatRel.repoName) + `/${flatRel.type}/` + slugify(flatRel.relName) + '/commits.xml'}" rel="self" />`),
m.trust(`<link href="${data.reposConfig.baseUrl + '/repos/' + slugify(flatRel.repoName) + `/${flatRel.type}/` + slugify(flatRel.relName)}" />`),
m('updated', dateToRfc3339(currentRepo.commits.get(currentRel.sha).date)),
m('id', data.reposConfig.baseUrl),
m('author',
m('name', `${currentRepo.name} contributors`)
),
currentRelCommits.map((commit) => {
const commitUrl = data.reposConfig.baseUrl + '/repos/' + slugify(flatRel.repoName) + `/${flatRel.type}/` + slugify(flatRel.relName) + '/commits/' + commit.commit.hash
return m('entry', [
m('title', commit.commit.message.split('\n')[0]),
m('author', m('name', commit.commit.author)),14 15 16 17 18 19
const languageExtension = eleventyConfig.getFilter("languageExtension")
const renderContentIfAvailable = eleventyConfig.getFilter("renderContentIfAvailable")
const currentBranch: Branch = data.currentBranch
const fileInfo: FlatFileEntry = data.fileInfo
const pageContent = [ 14 15 16 17 18 19
const languageExtension = eleventyConfig.getFilter("languageExtension")
const renderContentIfAvailable = eleventyConfig.getFilter("renderContentIfAvailable")
const currentRel: Branch = data.currentRel
const fileInfo: FlatFileEntry = data.fileInfo
const pageContent = [ 95 96 97 98 99 100
m('div', {class: "row rendered-content"},
m('div', {class: "col"},
m.trust(await renderContentIfAvailable(
currentBranch.fileList.get(fileInfo.file).fileInfo.contents
))
)
)95 96 97 98 99 100
m('div', {class: "row rendered-content"},
m('div', {class: "col"},
m.trust(await renderContentIfAvailable(
currentRel.fileList.get(fileInfo.file).fileInfo.contents
))
)
)115 116 117 118 119 120
m('div', {class: "col-auto p-0"},
m('code', {style: "white-space: pre;"},
m('pre', {class: "language-text"},
lineNumbers(currentBranch.fileList.get(fileInfo.file).fileInfo.contents).map((lineNumber) => {
return lineNumber
}).join('\n')
)115 116 117 118 119 120
m('div', {class: "col-auto p-0"},
m('code', {style: "white-space: pre;"},
m('pre', {class: "language-text"},
lineNumbers(currentRel.fileList.get(fileInfo.file).fileInfo.contents).map((lineNumber) => {
return lineNumber
}).join('\n')
)124 125 126 127 128 129 130 131 132 133 134 135 136
m('div', {id: "annotations", class: "col-auto d-none p-0"},
m('code', {style: "white-space: pre;"}, [
m('pre', {class: "language-text"}, m.trust(
currentBranch.fileList.get(fileInfo.file).fileInfo.blameLines.map((annotation) => {
return `<a href="${data.reposPath}/${slugify(fileInfo.repoName)}/branches/${slugify(fileInfo.branchName)}/commits/${annotation.sha}">${annotation.sha.substr(0, 6)}</a> ${annotation.author}`
}).join('\n')))
])
),
m('div', {class: "col overflow-scroll p-0"},
m('code', m.trust(highlightCode(
currentBranch.fileList.get(fileInfo.file).fileInfo.contents,
languageExtension(
fileInfo.file,
fileInfo.repoName124 125 126 127 128 129 130 131 132 133 134 135 136
m('div', {id: "annotations", class: "col-auto d-none p-0"},
m('code', {style: "white-space: pre;"}, [
m('pre', {class: "language-text"}, m.trust(
currentRel.fileList.get(fileInfo.file).fileInfo.blameLines.map((annotation) => {
return `<a href="${data.reposPath}/${slugify(fileInfo.repoName)}/branches/${slugify(fileInfo.branchName)}/commits/${annotation.sha}">${annotation.sha.substr(0, 6)}</a> ${annotation.author}`
}).join('\n')))
])
),
m('div', {class: "col overflow-scroll p-0"},
m('code', m.trust(highlightCode(
currentRel.fileList.get(fileInfo.file).fileInfo.contents,
languageExtension(
fileInfo.file,
fileInfo.repoName2 3 4 5 6 7
import htmlPage from './common/htmlPage.ts'
export default async (reposConfig: any, eleventyConfig: any, data: any) => {
const branch: Repository['branches'][0] = data.currentBranch
const topLevelFilesOnly = eleventyConfig.getFilter("topLevelFilesOnly")
const slugify = eleventyConfig.getFilter("slugify")
2 3 4 5 6 7
import htmlPage from './common/htmlPage.ts'
export default async (reposConfig: any, eleventyConfig: any, data: any) => {
const branch: Repository['branches'][0] = data.currentRel
const topLevelFilesOnly = eleventyConfig.getFilter("topLevelFilesOnly")
const slugify = eleventyConfig.getFilter("slugify")
13 14 15 16 17 18
m('div', {class: "col"},
m('p', [
'Files snapshot from ',
m('span', {class: "font-monospace"}, data.branchInfo.branchName)
])
)
]),13 14 15 16 17 18
m('div', {class: "col"},
m('p', [
'Files snapshot from ',
m('span', {class: "font-monospace"}, data.flatRel.relName)
])
)
]),26 27 28 29 30 31
return m('li', {class: 'list-group-item'}, [
file.isDirectory ? m.trust('<span>📁 </span>') : null,
m('a', {
href: `${data.reposPath}/${slugify(data.branchInfo.repoName)}/branches/${slugify(data.branchInfo.branchName)}/files/${
file.fullPath.split('/')
.map((pathPart) => {
return pathPart.split('.').map((subPart) => {26 27 28 29 30 31
return m('li', {class: 'list-group-item'}, [
file.isDirectory ? m.trust('<span>📁 </span>') : null,
m('a', {
href: `${data.reposPath}/${slugify(data.flatRel.repoName)}/branches/${slugify(data.flatRel.relName)}/files/${
file.fullPath.split('/')
.map((pathPart) => {
return pathPart.split('.').map((subPart) => {2 3 4 5 6 7
export default async (eleventyConfig: any) => {
return async (data) => {
const branch: Branch = data.currentBranch
return branch.fileList.get(data.fileInfo.file).fileInfo.contents
}2 3 4 5 6 7
export default async (eleventyConfig: any) => {
return async (data) => {
const branch: Branch = data.currentRel
return branch.fileList.get(data.fileInfo.file).fileInfo.contents
}4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
export default async (reposConfig: any, eleventyConfig: any, data: any) => {
const repo: Repository = data.currentRepo
const branch: Repository['branches'][0] = data.currentBranch
const renderContentIfAvailable = eleventyConfig.getFilter("renderContentIfAvailable")
const slugify = eleventyConfig.getFilter("slugify")
const getReadMe = eleventyConfig.getFilter("getReadMe")
const latestCommit = repo.commits.get(branch.head)
const latestCommitMessage = latestCommit.message.length > 72
? latestCommit.message.split('\n')[0].substr(0, 72) + '...'
: latestCommit.message
const nav = NavHelper(reposConfig, slugify, repo.name, branch.name)
const languageCounts = Array.from(branch.fileList.keys()).reduce((counts, currentFile) => {
const fileParts = currentFile.split(".")
const fileExtension = fileParts[fileParts.length - 1]
// todo: add more ignoreable extensions or specific files4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
export default async (reposConfig: any, eleventyConfig: any, data: any) => {
const repo: Repository = data.currentRepo
const rel: Repository['branches'][0] | Repository['tags'][0] = data.currentRel
const renderContentIfAvailable = eleventyConfig.getFilter("renderContentIfAvailable")
const slugify = eleventyConfig.getFilter("slugify")
const getReadMe = eleventyConfig.getFilter("getReadMe")
const latestCommit = repo.commits.get(rel.sha)
const latestCommitMessage = latestCommit.message.length > 72
? latestCommit.message.split('\n')[0].substr(0, 72) + '...'
: latestCommit.message
const nav = NavHelper(reposConfig, slugify, repo.name, rel.name)
const languageCounts = Array.from(rel.fileList.keys()).reduce((counts, currentFile) => {
const fileParts = currentFile.split(".")
const fileExtension = fileParts[fileParts.length - 1]
// todo: add more ignoreable extensions or specific files30 31 32 33 34 35
// todo: this is probably broken for repos that use fewer than 6 languages
let languagePercentages: Array<[string, number]> = []
const total = Array.from(branch.fileList.keys()).length
for (const entry of languageCounts) {
languagePercentages.push([entry[0], entry[1] / total])30 31 32 33 34 35
// todo: this is probably broken for repos that use fewer than 6 languages
let languagePercentages: Array<[string, number]> = []
const total = Array.from(rel.fileList.keys()).length
for (const entry of languageCounts) {
languagePercentages.push([entry[0], entry[1] / total])45 46 47 48 49 50
const largestPercent = Math.max(...topLanguagePercentages.map(tuple => tuple[1]), otherLanguagePercent)
const readmeContent = await renderContentIfAvailable(await getReadMe(repo.name, branch.name), branch.name)
const pageContent = [
m('div', {class: "row"}, [45 46 47 48 49 50
const largestPercent = Math.max(...topLanguagePercentages.map(tuple => tuple[1]), otherLanguagePercent)
const readmeContent = await renderContentIfAvailable(await getReadMe(repo.name, rel.name), rel.name)
const pageContent = [
m('div', {class: "row"}, [1 2 3 4 5 6
import util from 'util'
import childProcess from 'child_process'
import {repos, getBranchesAndTags} from './src/repos.ts'
import branches from './src/branches.ts'
import flatFiles from './src/flatFiles.ts'
import flatPatches from './src/flatPatches.ts'
import paginatedPatches, {type PatchPage} from './src/paginatedPatches.ts'1 2 3 4 5 6
import util from 'util'
import childProcess from 'child_process'
import {repos, getBranchesAndTags} from './src/repos.ts'
import getFlatRels from './src/flatRels.ts'
import flatFiles from './src/flatFiles.ts'
import flatPatches from './src/flatPatches.ts'
import paginatedPatches, {type PatchPage} from './src/paginatedPatches.ts'87 88 89 90 91 92 93
eleventyConfig.addGlobalData("reposConfig", reposConfiguration)
eleventyConfig.addGlobalData("reposPath", reposPath)
const branchesData = branches(reposData)
eleventyConfig.addGlobalData("branches", branchesData)
eleventyConfig.addFilter("getFileName", (filePath: string) => {
const pathParts = filePath.split("/")87 88 89 90 91 92 93
eleventyConfig.addGlobalData("reposConfig", reposConfiguration)
eleventyConfig.addGlobalData("reposPath", reposPath)
const flatRels = getFlatRels(reposData)
eleventyConfig.addGlobalData("flatRels", flatRels)
eleventyConfig.addFilter("getFileName", (filePath: string) => {
const pathParts = filePath.split("/")252 253 254 255 256 257 258
return isDirectory
})
eleventyConfig.addFilter("pagesJustForBranch", (pages: Array<PatchPage>, repoName: string, branchName: string) => {
return pages.filter(page => page.repoName === repoName && page.branchName === branchName)
})
eleventyConfig.addFilter("date", (dateString: string) => {252 253 254 255 256 257 258
return isDirectory
})
eleventyConfig.addFilter("pagesJustForRel", (pages: Array<PatchPage>, repoName: string, relName: string, relType: "branch" | "tag") => {
return pages.filter(page => page.repoName === repoName && page.relName === relName && page.type === relType)
})
eleventyConfig.addFilter("date", (dateString: string) => {294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
commonPage(branchesJsTemplate, reposConfiguration, eleventyConfig),
{
pagination: {
data: "branches",
size: 1,
alias: "branchInfo",
},
permalink: (data) => {
const repoName = data.branchInfo.repoName
const branchName = data.branchInfo.branchName
return `${reposPath}/${eleventyConfig.getFilter("slugify")(repoName)}/branches/${eleventyConfig.getFilter("slugify")(branchName)}/branches/`
},
eleventyComputed: {
nav: {
repoName: (data) => data.branchInfo.repoName,
branchName: (data) => data.branchInfo.branchName,
path: "branches"
},
currentRepo: (data) => reposData.find(repo => {
return repo.name === data.branchInfo.repoName
}),
currentBranch: (data) => reposData.find(repo => {
return repo.name === data.branchInfo.repoName
}).branches.find(branch => {
return branch.name === data.branchInfo.branchName
}),
},
navTab: "branches",
}294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
commonPage(branchesJsTemplate, reposConfiguration, eleventyConfig),
{
pagination: {
data: "flatRels",
size: 1,
alias: "flatRel",
},
permalink: (data) => {
const repoName = data.flatRel.repoName
const relName = data.flatRel.relName
const relType = data.flatRel.type
return `${reposPath}/${eleventyConfig.getFilter("slugify")(repoName)}/${relType}/${eleventyConfig.getFilter("slugify")(relName)}/branches/`
},
eleventyComputed: {
nav: {
repoName: (data) => data.flatRel.repoName,
relName: (data) => data.flatRel.relName,
path: "branches"
},
currentRepo: (data) => reposData.find(repo => {
return repo.name === data.flatRel.repoName
}),
currentRel: (data) => {
const relType = data.flatRel.type === "branch" ? "branches" : "tags"
const currentRepo = reposData.find(repo => {
return repo.name === data.flatRel.repoName
})
return currentRepo[relType].find(rel => {
return rel.name === data.flatRel.relName
})
}
},
navTab: "branches",
}337 338 339 340 341 342
permalink: (data) => {
const repoName = data.fileInfo.repoName
const branchName = data.fileInfo.branchName
return `${reposPath}/${eleventyConfig.getFilter("slugify")(repoName)}/branches/${eleventyConfig.getFilter("slugify")(branchName)}/files/${data.fileInfo.file.split('/').map((filePart) => filePart.split('.').map((subPart) => eleventyConfig.getFilter("slugify")(subPart)).join('.')).join('/')}.html`
},
eleventyComputed: {
nav: {337 338 339 340 341 342 343
permalink: (data) => {
const repoName = data.fileInfo.repoName
const branchName = data.fileInfo.branchName
const relType = data.fileInfo.type
return `${reposPath}/${eleventyConfig.getFilter("slugify")(repoName)}/${relType}/${eleventyConfig.getFilter("slugify")(branchName)}/files/${data.fileInfo.file.split('/').map((filePart) => filePart.split('.').map((subPart) => eleventyConfig.getFilter("slugify")(subPart)).join('.')).join('/')}.html`
},
eleventyComputed: {
nav: {348 349 350 351 352 353
currentRepo: (data) => reposData.find(repo => {
return repo.name === data.fileInfo.repoName
}),
currentBranch: (data) => reposData.find(repo => {
return repo.name === data.fileInfo.repoName
}).branches.find(branch => {
return branch.name === data.fileInfo.branchName348 349 350 351 352 353
currentRepo: (data) => reposData.find(repo => {
return repo.name === data.fileInfo.repoName
}),
currentRel: (data) => reposData.find(repo => {
return repo.name === data.fileInfo.repoName
}).branches.find(branch => {
return branch.name === data.fileInfo.branchName373 374 375 376 377 378 379 380 381
permalink: (data) => {
const repoName = data.fileInfo.repoName
const branchName = data.fileInfo.branchName
return `${reposPath}/${eleventyConfig.getFilter("slugify")(repoName)}/branches/${eleventyConfig.getFilter("slugify")(branchName)}/raw/${data.fileInfo.file.split('.').map(filePart => eleventyConfig.getFilter("slugify")(filePart)).join('.')}`
},
eleventyComputed: {
currentBranch: (data) => reposData.find(repo => {
return repo.name === data.fileInfo.repoName
}).branches.find(branch => {
return branch.name === data.fileInfo.branchName373 374 375 376 377 378 379 380 381 382
permalink: (data) => {
const repoName = data.fileInfo.repoName
const branchName = data.fileInfo.branchName
const relType = data.fileInfo.type
return `${reposPath}/${eleventyConfig.getFilter("slugify")(repoName)}/${relType}/${eleventyConfig.getFilter("slugify")(branchName)}/raw/${data.fileInfo.file.split('.').map(filePart => eleventyConfig.getFilter("slugify")(filePart)).join('.')}`
},
eleventyComputed: {
currentRel: (data) => reposData.find(repo => {
return repo.name === data.fileInfo.repoName
}).branches.find(branch => {
return branch.name === data.fileInfo.branchName391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418
commonPage(filesJsTemplate, reposConfiguration, eleventyConfig),
{
pagination: {
data: "branches",
size: 1,
alias: "branchInfo",
},
permalink: (data) => {
const repoName = data.branchInfo.repoName
const branchName = data.branchInfo.branchName
return `${reposPath}/${eleventyConfig.getFilter("slugify")(repoName)}/branches/${eleventyConfig.getFilter("slugify")(branchName)}/files/`
},
eleventyComputed: {
nav: {
repoName: (data) => data.branchInfo.repoName,
branchName: (data) => data.branchInfo.branchName,
path: "files",
},
currentRepo: (data) => reposData.find(repo => {
return repo.name === data.branchInfo.repoName
}),
currentBranch: (data) => reposData.find(repo => {
return repo.name === data.branchInfo.repoName
}).branches.find(branch => {
return branch.name === data.branchInfo.branchName
})
},
navTab: "files",
}391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422
commonPage(filesJsTemplate, reposConfiguration, eleventyConfig),
{
pagination: {
data: "flatRels",
size: 1,
alias: "flatRel",
},
permalink: (data) => {
const repoName = data.flatRel.repoName
const relName = data.flatRel.relName
const relType = data.flatRel.type
return `${reposPath}/${eleventyConfig.getFilter("slugify")(repoName)}/${relType}/${eleventyConfig.getFilter("slugify")(relName)}/files/`
},
eleventyComputed: {
nav: {
repoName: (data) => data.flatRel.repoName,
relName: (data) => data.flatRel.relName,
path: "files",
},
currentRepo: (data) => reposData.find(repo => {
return repo.name === data.flatRel.repoName
}),
currentRel: (data) => {
const relType = data.flatRel.type === "branch" ? "branches" : "tags"
return reposData.find(repo => {
return repo.name === data.flatRel.repoName
})[relType].find(rel => {
return rel.name === data.flatRel.relName
})
}
},
navTab: "files",
}425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452
commonPage(repoJsTemplate, reposConfiguration, eleventyConfig),
{
pagination: {
data: "branches",
size: 1,
alias: "branch",
},
permalink: (data) => {
const repoName = data.branch.repoName
const branchName = data.branch.branchName
return `${reposPath}/${eleventyConfig.getFilter("slugify")(repoName)}/branches/${eleventyConfig.getFilter("slugify")(branchName)}/`
},
eleventyComputed: {
nav: {
repoName: (data) => data.branch.repoName,
branchName: (data) => data.branch.branchName,
path: (data) => '', // ask about why empty string here shows up as a function
},
currentRepo: (data) => reposData.find(repo => {
return repo.name === data.branch.repoName
}),
currentBranch: (data) => reposData.find(repo => {
return repo.name === data.branch.repoName
}).branches.find(branch => {
return branch.name === data.branch.branchName
}),
},
navTab: "home"
}425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456
commonPage(repoJsTemplate, reposConfiguration, eleventyConfig),
{
pagination: {
data: "flatRels",
size: 1,
alias: "flatRel",
},
permalink: (data) => {
const repoName = data.flatRel.repoName
const relName = data.flatRel.relName
const relType = data.flatRel.type
return `${reposPath}/${eleventyConfig.getFilter("slugify")(repoName)}/${relType}/${eleventyConfig.getFilter("slugify")(relName)}/`
},
eleventyComputed: {
nav: {
repoName: (data) => data.flatRel.repoName,
relName: (data) => data.flatRel.relName,
path: (data) => '', // TODO: ask about why empty string here shows up as a function
},
currentRepo: (data) => reposData.find(repo => {
return repo.name === data.flatRel.repoName
}),
currentRel: (data) => {
const relType = data.flatRel.type === "branch" ? "branches" : "tags"
return reposData.find(repo => {
return repo.name === data.flatRel.repoName
})[relType].find(rel => {
return rel.name === data.flatRel.relName
})
}
},
navTab: "home"
}456 457 458 459 460 461
// COMMITS.TS
const paginatedPatchesData = await paginatedPatches(reposData)
eleventyConfig.addTemplate(
`repos/patches.11ty.js`,
commonPage(commitsJsTemplate, reposConfiguration, eleventyConfig),
{
pagination: {456 457 458 459 460 461
// COMMITS.TS
const paginatedPatchesData = await paginatedPatches(reposData)
eleventyConfig.addTemplate(
`repos/commits.11ty.js`,
commonPage(commitsJsTemplate, reposConfiguration, eleventyConfig),
{
pagination: {467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488
paginatedPatches: paginatedPatchesData,
permalink: (data) => {
const repoName = data.patchPage.repoName
const branchName = data.patchPage.branchName
return `${reposPath}/${eleventyConfig.getFilter("slugify")(repoName)}/branches/${eleventyConfig.getFilter("slugify")(branchName)}/commits/page${data.patchPage.pageNumber}/`
},
eleventyComputed: {
nav: {
repoName: (data) => data.patchPage.repoName,
branchName: (data) => data.patchPage.branchName,
path: 'commits/page1',
},
currentRepo: (data) => reposData.find(repo => {
return repo.name === data.patchPage.repoName
}),
currentBranch: (data) => reposData.find(repo => {
return repo.name === data.patchPage.repoName
}).branches.find(branch => {
return branch.name === data.patchPage.branchName
}),
},
navTab: "commits",
}467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492
paginatedPatches: paginatedPatchesData,
permalink: (data) => {
const repoName = data.patchPage.repoName
const relName = data.patchPage.relName
const relType = data.patchPage.type
return `${reposPath}/${eleventyConfig.getFilter("slugify")(repoName)}/${relType}/${eleventyConfig.getFilter("slugify")(relName)}/commits/page${data.patchPage.pageNumber}/`
},
eleventyComputed: {
nav: {
repoName: (data) => data.patchPage.repoName,
relName: (data) => data.patchPage.relName,
path: 'commits/page1',
},
currentRepo: (data) => reposData.find(repo => {
return repo.name === data.patchPage.repoName
}),
currentRel: (data) => {
const relType = data.patchPage.type === "branch" ? "branches" : "tags"
return reposData.find(repo => {
return repo.name === data.patchPage.repoName
})[relType].find(rel => {
return rel.name === data.patchPage.relName
})
}
},
navTab: "commits",
}503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524
flatPatches: flatPatchesData,
permalink: (data) => {
const repoName = data.patchInfo.repoName
const branchName = data.patchInfo.branchName
return `${reposPath}/${eleventyConfig.getFilter("slugify")(repoName)}/branches/${eleventyConfig.getFilter("slugify")(branchName)}/commits/${data.patchInfo.commit.hash}/`
},
eleventyComputed: {
nav: {
repoName: (data) => data.patchInfo.repoName,
branchName: (data) => data.patchInfo.branchName,
path: 'commits/page1',
},
currentRepo: (data) => reposData.find(repo => {
return repo.name === data.patchInfo.repoName
}),
currentBranch: (data) => reposData.find(repo => {
return repo.name === data.patchInfo.repoName
}).branches.find(branch => {
return branch.name === data.patchInfo.branchName
}),
},
navTab: "commits",
}503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528
flatPatches: flatPatchesData,
permalink: (data) => {
const repoName = data.patchInfo.repoName
const relName = data.patchInfo.relName
const relType = data.patchInfo.type
return `${reposPath}/${eleventyConfig.getFilter("slugify")(repoName)}/${relType}/${eleventyConfig.getFilter("slugify")(relName)}/commits/${data.patchInfo.commit.hash}/`
},
eleventyComputed: {
nav: {
repoName: (data) => data.patchInfo.repoName,
relName: (data) => data.patchInfo.relName,
path: 'commits/page1',
},
currentRepo: (data) => reposData.find(repo => {
return repo.name === data.patchInfo.repoName
}),
currentRel: (data) => {
const relType = data.patchInfo.type === "branch" ? "branches" : "tags"
return reposData.find(repo => {
return repo.name === data.patchInfo.repoName
})[relType].find(rel => {
return rel.name === data.patchInfo.relName
})
},
},
navTab: "commits",
}531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556
feedJsTemplate(eleventyConfig),
{
pagination: {
data: "branches",
size: 1,
alias: "branch",
},
permalink: (data) => {
const repoName = data.branch.repoName
const branchName = data.branch.branchName
return `${reposPath}/${eleventyConfig.getFilter("slugify")(repoName)}/branches/${eleventyConfig.getFilter("slugify")(branchName)}/commits.xml`
},
eleventyComputed: {
currentRepo: (data) => reposData.find(repo => {
return repo.name === data.branch.repoName
}),
currentBranch: (data) => reposData.find(repo => {
return repo.name === data.branch.repoName
}).branches.find(branch => {
return branch.name === data.branch.branchName
}),
currentBranchCommits: (data) => {
return flatPatchesData.filter((patch) => {
return patch.branchName === data.branch.branchName
})
}
},531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560
feedJsTemplate(eleventyConfig),
{
pagination: {
data: "flatRels",
size: 1,
alias: "flatRel",
},
permalink: (data) => {
const repoName = data.flatRel.repoName
const relName = data.flatRel.relName
const relType = data.flatRel.type
return `${reposPath}/${eleventyConfig.getFilter("slugify")(repoName)}/${relType}/${eleventyConfig.getFilter("slugify")(relName)}/commits.xml`
},
eleventyComputed: {
currentRepo: (data) => reposData.find(repo => {
return repo.name === data.flatRel.repoName
}),
currentRel: (data) => {
const relType = data.flatRel.type === "branch" ? "branches" : "tags"
return reposData.find(repo => {
return repo.name === data.flatRel.repoName
})[relType].find(rel => {
return rel.name === data.flatRel.relName
})
},
currentRelCommits: (data) => {
return flatPatchesData.filter((patch) => {
return patch.relName === data.flatRel.relName
})
}
},0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
import { type Repository } from "./dataTypes.ts"
let cachedBranches: Array<{
branchName: string,
repoName: string,
}> | null = null
export default (repos: Array<Repository>) => {
if (cachedBranches !== null) { return cachedBranches }
cachedBranches = repos.flatMap((repo) => {
return repo.branches.map((branch) => {
const result = {
branchName: branch.name,
repoName: repo.name,
compareTo: branch.compareTo,
ahead: branch.ahead,
behind: branch.behind,
}
if (branch.description) { result['description'] = branch.description }
return result
})
})
return cachedBranches
}
16 17 18 19 20 21
ahead: number,
behind: number,
compareTo: string,
head: string,
// instead of strings, make it point to a function that returns
// the file contents. The first time, search in both directions for a commit
// that has already cached this file before a diff in that commit mentions16 17 18 19 20 21
ahead: number,
behind: number,
compareTo: string,
sha: string,
// instead of strings, make it point to a function that returns
// the file contents. The first time, search in both directions for a commit
// that has already cached this file before a diff in that commit mentions2 3 4 5 6 7
type FlatPatchRecord = {
commit: ReturnType<Repository['commits']['get']>,
repoName: string,
branchName: string,
}
let cachedFlatPatches: Array<FlatPatchRecord> | null = null2 3 4 5 6 7 8
type FlatPatchRecord = {
commit: ReturnType<Repository['commits']['get']>,
repoName: string,
relName: string,
type: "branch" | "tag",
}
let cachedFlatPatches: Array<FlatPatchRecord> | null = null12 13 14 15 16 17 18 19 20 21 22 23 24 25
if (cachedFlatPatches !== null) { return cachedFlatPatches }
cachedFlatPatches = repos.flatMap((repo) => {
return repo.branches.flatMap((branch) => {
const flatPatches: Array<FlatPatchRecord> = []
let currentCommit: ReturnType<Repository['commits']['get']> | undefined = repo.commits.get(branch.head)
while (currentCommit !== undefined) {
flatPatches.push({
commit: currentCommit,
repoName: repo.name,
branchName: branch.name
})
currentCommit = repo.commits.get(currentCommit.parent)12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
if (cachedFlatPatches !== null) { return cachedFlatPatches }
cachedFlatPatches = repos.flatMap((repo) => {
const branches = repo.branches.flatMap((branch) => {
const flatPatches: Array<FlatPatchRecord> = []
let currentCommit: ReturnType<Repository['commits']['get']> | undefined = repo.commits.get(branch.sha)
while (currentCommit !== undefined) {
flatPatches.push({
type: "branch",
commit: currentCommit,
repoName: repo.name,
relName: branch.name
})
currentCommit = repo.commits.get(currentCommit.parent)28 29 30 31 32
return flatPatches
})
})
return cachedFlatPatches28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
return flatPatches
})
const tags = repo.tags.flatMap((tag) => {
const flatPatches: Array<FlatPatchRecord> = []
let currentCommit: ReturnType<Repository['commits']['get']> = repo.commits.get(tag.sha)
while (currentCommit !== undefined) {
flatPatches.push({
type: "tag",
commit: currentCommit,
repoName: repo.name,
relName: tag.name,
})
currentCommit = repo.commits.get(currentCommit.parent)
}
return flatPatches
})
return [...branches, ...tags]
})
return cachedFlatPatches
-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
import { type Repository } from "./dataTypes.ts"
type BranchEntry = {
relName: string,
type: "branch",
repoName: string,
compareTo: string,
ahead: number,
behind: number,
}
type TagEntry = {
relName: string,
type: "tag",
repoName: string,
}
let cachedRels: Array<BranchEntry | TagEntry> | null = null
export default (repos: Array<Repository>) => {
if (cachedRels !== null) { return cachedRels }
cachedRels = repos.flatMap((repo) => {
const branches: Array<BranchEntry> = repo.branches.map((branch) => {
const result: BranchEntry = {
relName: branch.name,
type: "branch",
repoName: repo.name,
compareTo: branch.compareTo,
ahead: branch.ahead,
behind: branch.behind,
}
if (branch.description) { result['description'] = branch.description }
return result
})
const tags: Array<TagEntry> = repo.tags.map((tag) => {
return {
relName: tag.name,
type: "tag",
repoName: repo.name,
}
})
return [...branches, ...tags]
})
return cachedRels
}2 3 4 5 6 7
export type PatchPage = {
repoName: string,
branchName: string,
commits: Array<ReturnType<Repository['commits']['get']>>,
pageNumber: number,
}2 3 4 5 6 7 8
export type PatchPage = {
repoName: string,
relName: string,
type: "branch" | "tag",
commits: Array<ReturnType<Repository['commits']['get']>>,
pageNumber: number,
}20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
const index = paginatedPatches.findIndex((page) => {
return (
page.repoName === patch.repoName
&& page.branchName == patch.branchName
&& page.commits.length <= patchesPerPage
)
})
if (index === -1) {
const pageNumber = paginatedPatches.filter(page => (page.repoName === patch.repoName && page.branchName === patch.branchName)).length + 1
paginatedPatches.push({
repoName: patch.repoName,
branchName: patch.branchName,
commits: [patch.commit],
// current page number is one more than "how many items in paginatedPatches already
// have repoName as their repo name.
pageNumber,20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
const index = paginatedPatches.findIndex((page) => {
return (
page.repoName === patch.repoName
&& page.relName == patch.relName
&& page.type == patch.type
&& page.commits.length <= patchesPerPage
)
})
if (index === -1) {
const pageNumber = paginatedPatches.filter(page => (
page.repoName === patch.repoName
&& page.relName === patch.relName
&& page.type === patch.type
)).length + 1
paginatedPatches.push({
repoName: patch.repoName,
relName: patch.relName,
commits: [patch.commit],
type: patch.type,
// current page number is one more than "how many items in paginatedPatches already
// have repoName as their repo name.
pageNumber,161 162 163 164 165 166
const branchHead = branchHeadRes.stdout.split(" ")[0]
const result = {
name: branch.name,
head: branchHead,
fileList: await getFileList(branch.name, repoLocation)
}
if (branch.description) { result['description'] = branch.description }161 162 163 164 165 166
const branchHead = branchHeadRes.stdout.split(" ")[0]
const result = {
name: branch.name,
sha: branchHead,
fileList: await getFileList(branch.name, repoLocation)
}
if (branch.description) { result['description'] = branch.description }189 190 191 192 193 194 195 196 197 198 199 200 201
const compareToBranch = branches.find((test) => test.name === compareTo)
const compareToBranchCommits = new Set<string>()
let currentCommit = commits.get(compareToBranch.head)
while (currentCommit !== undefined) {
compareToBranchCommits.add(currentCommit.hash)
currentCommit = commits.get(currentCommit.parent)
}
const thisBranchCommits = new Set<string>()
currentCommit = commits.get(branch.head)
while (currentCommit !== undefined) {
thisBranchCommits.add(currentCommit.hash)
currentCommit = commits.get(currentCommit.parent)189 190 191 192 193 194 195 196 197 198 199 200 201
const compareToBranch = branches.find((test) => test.name === compareTo)
const compareToBranchCommits = new Set<string>()
let currentCommit = commits.get(compareToBranch.sha)
while (currentCommit !== undefined) {
compareToBranchCommits.add(currentCommit.hash)
currentCommit = commits.get(currentCommit.parent)
}
const thisBranchCommits = new Set<string>()
currentCommit = commits.get(branch.sha)
while (currentCommit !== undefined) {
thisBranchCommits.add(currentCommit.hash)
currentCommit = commits.get(currentCommit.parent)