Tucker McKnight <tmcknight@instructure.com> | Sun Apr 19 2026
Use the flatRels object on all templates Fixes bugs with pages not getting generated for tags. Tag pages, and having links properly point to them, seems to be working in all places now.
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)}/branch/${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', [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
data: any,
) => {
const slugify = eleventyConfig.getFilter("slugify")
const nav = NavHelper({
reposConfig,
slugify,
currentRepoName: data.flatRef.repoName,
currentRefName: data.flatRef.refName,
currentRefType: data.flatRef.type,
})
const branchesWithWorkInProgress = data.flatRefs.filter(ref => ref.type === "branch" && ref.ahead > 0)
const branchesFullyMerged = data.flatRefs.filter(ref => ref.type === "branch" && ref.ahead === 0)
const branchCards = (branches) => {
return m('ul', branches.map((branch) => {
return branch.repoName === data.flatRef.repoName ?
m('li', [
m('a', {
href: `${data.reposPath}/${slugify(branch.repoName)}/branch/${slugify(branch.refName)}/branches`
}, branch.refName),
branch.refName === data.flatRef.refName
? m('div', {class: "badge rounded-pill bg-secondary mx-1"}, 'current') : null,
branch.refName === 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
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"},6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
eleventyConfig: any,
data: any,
) => {
const pagesJustForRef = eleventyConfig.getFilter("pagesJustForRef")
const slugify = eleventyConfig.getFilter("slugify")
const date = eleventyConfig.getFilter("date")
const nav = NavHelper({
reposConfig,
slugify,
currentRepoName: data.patchPage.repoName,
currentRefName: data.patchPage.refName,
currentRefType: data.patchPage.type,
})
const pageContent = [
m('div', {class: "row mt-3 mb-1"},20 21 22 23 24 25
m('span', {class: "font-monospace me-2"}, data.patchPage.relName),
m('a', {
class: "btn btn-outline-primary badge shadow-none",
href: nav.repoCurrentBranchRssFeed()
}, [
m('img', {
src: `${nav.rootPath()}frontend/img/rss-icon.svg`,20 21 22 23 24 25
m('span', {class: "font-monospace me-2"}, data.patchPage.relName),
m('a', {
class: "btn btn-outline-primary badge shadow-none",
href: nav.rssFeed()
}, [
m('img', {
src: `${nav.rootPath()}frontend/img/rss-icon.svg`,33 34 35 36 37 38
),
m('nav',
m('ul', {class: "pagination"},
pagesJustForRel(
data.paginatedPatches,
data.patchPage.repoName,
data.patchPage.relName,33 34 35 36 37 38
),
m('nav',
m('ul', {class: "pagination"},
pagesJustForRef(
data.paginatedPatches,
data.patchPage.repoName,
data.patchPage.relName,67 68 69 70 71 72
})),
m('nav',
m('ul', {class: "pagination"},
pagesJustForRel(
data.paginatedPatches,
data.patchPage.repoName,
data.patchPage.relName,67 68 69 70 71 72
})),
m('nav',
m('ul', {class: "pagination"},
pagesJustForRef(
data.paginatedPatches,
data.patchPage.repoName,
data.patchPage.relName,10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
}
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 {
name: tag.name,
href: nav.repoTagHome(tag.name) + '/' + data.nav.path,
date: repo.commits.get(tag.sha).date.toISOString(),
}
}))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
}
const repo: Repository = data.currentRepo
const ref: Repository['branches'][0] = data.currentRef
const slugify = eleventyConfig.getFilter("slugify")
const nav = NavHelper({
reposConfig,
slugify,
currentRepoName: repo.name,
currentRefName: ref.name,
currentRefType: data.currentRefType,
})
const branchesWithHrefs = repo.branches.map((branch) => {
return {
name: branch.name,
href: nav.refHome({refName: branch.name, refType: 'branch'}) + '/' + data.nav.path,
date: repo.commits.get(branch.sha).date.toISOString(),
}
}).concat(repo.tags.map((tag) => {
return {
name: tag.name,
href: nav.refHome({refName: tag.name, refType: 'tag'}) + '/' + data.nav.path,
date: repo.commits.get(tag.sha).date.toISOString(),
}
}))36 37 38 39 40 41
m('script', m.trust(`
window.branchesWithHrefs = ${JSON.stringify(branchesWithHrefs)};
window.defaultBranch = "${repo.defaultBranch}";
window.currentBranch = "${rel.name}";
window.cloneUrl = "${repo.cloneUrl}";
`)),
m('link', {36 37 38 39 40 41
m('script', m.trust(`
window.branchesWithHrefs = ${JSON.stringify(branchesWithHrefs)};
window.defaultBranch = "${repo.defaultBranch}";
window.currentBranch = "${ref.name}";
window.cloneUrl = "${repo.cloneUrl}";
`)),
m('link', {57 58 59 60 61 62
m('li', {class: "nav-item"},
m('a', {
class: "nav-link",
href: nav.repoCurrentBranchHome()
}, repo.name)
),
m('li', {class: "nav-item"}, [57 58 59 60 61 62
m('li', {class: "nav-item"},
m('a', {
class: "nav-link",
href: nav.refHome()
}, repo.name)
),
m('li', {class: "nav-item"}, [68 69 70 71 72 73
'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..."}),68 69 70 71 72 73
'data-bs-toggle': "dropdown",
'data-bs-auto-close': "outside",
'aria-expanded': "false"
}, ref.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..."}),92 93 94 95 96 97
]),
m('div', {class: "dropdown-divider"}),
m('div', {id: "dropdown-branches-results", class: "dropdown-branches"},
branchesListItems(branchesWithHrefs, repo.defaultBranch, rel.name, 'date')
)
])
])92 93 94 95 96 97
]),
m('div', {class: "dropdown-divider"}),
m('div', {id: "dropdown-branches-results", class: "dropdown-branches"},
branchesListItems(branchesWithHrefs, repo.defaultBranch, ref.name, 'date')
)
])
])105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
m('nav', {class: "navbar navbar-expand"},
m('ul', {class: "main-nav navbar-nav flex-wrap"}, [
m('li', {class: "nav-item"},
m('a', {class: `nav-link ${data.navTab === 'home' ? 'active' : ''}`, 'aria-current': "page", href: nav.repoCurrentBranchHome()}, 'Home')
),
m('li', {class: "nav-item"},
m('a', {class: `nav-link ${data.navTab === 'files' ? 'active' : ''}`, href: nav.repoCurrentBranchFiles()}, 'Files')
),
m('li', {class: "nav-item"},
m('a', {class: `nav-link ${data.navTab === 'commits' ? 'active' : ''}`, href: nav.repoCurrentBranchCommits()}, 'Commits')
),
m('li', {class: "nav-item"},
m('a', {class: `nav-link ${data.navTab === 'branches' ? 'active' : ''}`, href: nav.repoCurrentBranchBranches()}, 'Branches')
),
m('li', {class: "nav-item"},
m('a', {class: `nav-link ${data.navTab === 'tags' ? 'active' : ''}`, href: nav.repoCurrentBranchTags()}, 'Tags')
)
])
)105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
m('nav', {class: "navbar navbar-expand"},
m('ul', {class: "main-nav navbar-nav flex-wrap"}, [
m('li', {class: "nav-item"},
m('a', {class: `nav-link ${data.navTab === 'home' ? 'active' : ''}`, 'aria-current': "page", href: nav.refHome()}, 'Home')
),
m('li', {class: "nav-item"},
m('a', {class: `nav-link ${data.navTab === 'files' ? 'active' : ''}`, href: nav.files()}, 'Files')
),
m('li', {class: "nav-item"},
m('a', {class: `nav-link ${data.navTab === 'commits' ? 'active' : ''}`, href: nav.commits()}, 'Commits')
),
m('li', {class: "nav-item"},
m('a', {class: `nav-link ${data.navTab === 'branches' ? 'active' : ''}`, href: nav.branches()}, 'Branches')
),
m('li', {class: "nav-item"},
m('a', {class: `nav-link ${data.navTab === 'tags' ? 'active' : ''}`, href: nav.tags()}, 'Tags')
)
])
)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 46 47 48 49 50 51 52 53 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 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
import m from 'mithril'
import { type ReposConfiguration } from '../../src/configTypes.ts'
import { type Repository } from '../../src/dataTypes.ts'
import { NavHelper } from '../helpers/nav.ts'
import branchesListItems from './branchesListItems.ts'
export default async (reposConfig: ReposConfiguration, eleventyConfig: any, data: any, pageContent: any) => {
// this still necessary?
if (data.currentRepo === '') {
return
}
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(),
}
})
<<<<<<< HEAD
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 {
name: tag.name,
href: "tbd",
date: repo.commits.get(tag.sha).date.toISOString(),
}
}))
console.log(branchesWithHrefs)
return `
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>${repo.name}</title>
<link rel="stylesheet" href="/css/design-board.css">
<script>
window.branchesWithHrefs = ${JSON.stringify(branchesWithHrefs)};
window.defaultBranch = "${repo.defaultBranch}";
window.currentBranch = "${branch.name}";
window.cloneUrl = "${repo.cloneUrl}";
</script>
<script src="${nav.rootPath()}frontend/top.js"></script>
<link rel="stylesheet" id="prism-theme" type="text/css" href="${data.reposPath}/vendor/prism.css" />
</head>
<body>
<div class="container">
<div class="row">
<div class="col">
<nav class="navbar navbar-expand">
<ul class="navbar-nav flex-wrap">
<li class="nav-item">
<a class="nav-link" href="${nav.rootPath()}">← All repositories</a>
</li>
<li class="nav-item">
<a class="nav-link" href="${nav.repoCurrentBranchHome()}">${repo.name}</a>
</li>
<li class="nav-item">
<span class="nav-link d-inline-block">Branch:</span>
<div class="branch-selector dropdown-center d-inline-block">
<button class="branches nav-link d-inline-block btn btn-bg dropdown-toggle" data-bs-toggle="dropdown" data-bs-auto-close="outside" aria-expanded="false">
${branch.name}
</button>
<div class="dropdown-menu">
<form class="mx-3 my-1">
<input type="text" class="form-control" id="dropdownBranchSearch" placeholder="Search branches...">
<div>
<div class="row mt-3">
<div class="col">
<label class="form-label">Sort by:</label>
</div>
</div>
<div class="sortRadioButtons">
<div class="sortRadioButton pe-1">
<input class="form-check-input sort-filter" type="radio" name="branchSort" value='date' id="branchSortByDate" checked>
<label class="form-check-label" for="branchSortByDate">
Last commit
</label>
</div>
<div class="sortRadioButton ps-1">
<input class="form-check-input sort-filter" type="radio" name="branchSort" value='name' id="branchSortByName">
<label class="form-check-label" for="branchSortByName">
Name
</label>
</div>
</div>
</div>
</form>
<div class="dropdown-divider"></div>
<div id="dropdown-branches-results" class="dropdown-branches">
${branchesListItems(branchesWithHrefs, repo.defaultBranch, branch.name, 'date')}
</div>
</div>
</div>
</li>
</ul>
</nav>
</div>
<div class="col-auto pt-2">
<div class="dropdown">
<button class="dropdown-toggle btn btn-bg" id="dark-mode-switch" type="button" data-bs-toggle="dropdown" aria-expanded="false">
<span>️</span>
</button>
<ul class="dropdown-menu">
<li><button class="btn shadow-none" data-theme-pref="light" onclick="toggleDarkMode(this)"><span class="me-1">🌞</span>Light</button></li>
<li><button class="btn shadow-none" data-theme-pref="dark" onclick="toggleDarkMode(this)"><span class="me-1">🌙</span>Dark</button></li>
<li><button class="btn shadow-none" data-theme-pref="auto" onclick="toggleDarkMode(this)"><span class="me-1">🖥️</span>Match OS</button></li>
</ul>
</div>
</div>
</div>
<div class="row">
<div class="col">
<nav class="navbar navbar-expand">
<ul class="main-nav navbar-nav flex-wrap">
<li class="nav-item">
<a class="nav-link ${data.navTab === 'home' ? 'active' : ''}" aria-current="page" href="${nav.repoCurrentBranchHome()}">Home</a>
</li>
<li class="nav-item">
<a class="nav-link ${data.navTab === 'files' ? 'active' : ''}" href="${nav.repoCurrentBranchFiles()}">Files</a>
</li>
<li class="nav-item">
<a class="nav-link ${data.navTab === 'commits' ? 'active' : ''}" href="${nav.repoCurrentBranchCommits()}">Commits</a>
</li>
<li class="nav-item">
<a class="nav-link ${data.navTab === 'branches' ? 'active' : ''}" href="${nav.repoCurrentBranchBranches()}">Branches</a>
</li>
</ul>
</nav>
</div>
</div>
<div class="row">
<div class="col-12">
${await pageContent(eleventyConfig, data, nav)}
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js" integrity="sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI" crossorigin="anonymous"></script>
<script src="${nav.rootPath()}frontend/main-frontend.bundle.js"></script>
</body>
</html>
`
=======
return {
rootPath: nav.rootPath(),
pageTitle: repo.name,
additionalHeadContent: [
m('script', m.trust(`
window.branchesWithHrefs = ${JSON.stringify(branchesWithHrefs)};
window.defaultBranch = "${repo.defaultBranch}";
window.currentBranch = "${branch.name}";
window.cloneUrl = "${repo.cloneUrl}";
`)),
m('link', {
rel: "stylesheet",
id: "prism-theme",
type: "text/css",
href: `${data.reposPath}/vendor/prism.css`
}),
],
navbarContent: m('nav', {class: "navbar navbar-expand"}, [
m('ul', {class: "navbar-nav flex-wrap"}, [
m('li', {class: "nav-item"},
m('a', {
class: "nav-link",
href: nav.rootPath()
}, m.trust('❮ Repos'))
),
m('li', {class: "nav-item"},
m('a', {
class: "nav-link",
href: nav.repoCurrentBranchHome()
}, repo.name)
),
m('li', {class: "nav-item"}, [
m('span', {class: "nav-link d-inline-block"}, 'Branch:'),
m('div', {class: "branch-selector dropdown-center d-inline-block"}, [
m('button', {
class: "branches nav-link d-inline-block btn btn-bg dropdown-toggle",
'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..."}),
m('div', [
m('div', {class: "row mt-3"},
m('div', {class: "col"},
m('label', {class: "form-label"}, 'Sort by:')
)
),
m('div', {class: "sortRadioButtons"}, [
m('div', {class: "sortRadioButton pe-1"}, [
m('input', {class: "form-check-input sort-filter", type: "radio", name: "branchSort", value: 'date', id: "branchSortByDate", checked: true}),
m('label', {class: "form-check-label", for: "branchSortByDate"}, 'Last commit')
]),
m('div', {class: "sortRadioButton ps-1"}, [
m('input', {class: "form-check-input sort-filter", type: "radio", name: "branchSort", value: 'name', id: "branchSortByName"}),
m('label', {class: "form-check-label", for: "branchSortByName"}, 'Name')
])
])
])
]),
m('div', {class: "dropdown-divider"}),
m('div', {id: "dropdown-branches-results", class: "dropdown-branches"},
branchesListItems(branchesWithHrefs, repo.defaultBranch, branch.name, 'date')
)
])
])
])
])
]),
pageContent: [
m('div', {class: "row"},
m('div', {class: "col"},
m('nav', {class: "navbar navbar-expand"},
m('ul', {class: "main-nav navbar-nav flex-wrap"}, [
m('li', {class: "nav-item"},
m('a', {class: `nav-link ${data.navTab === 'home' ? 'active' : ''}`, 'aria-current': "page", href: nav.repoCurrentBranchHome()}, 'Home')
),
m('li', {class: "nav-item"},
m('a', {class: `nav-link ${data.navTab === 'files' ? 'active' : ''}`, href: nav.repoCurrentBranchFiles()}, 'Files')
),
m('li', {class: "nav-item"},
m('a', {class: `nav-link ${data.navTab === 'commits' ? 'active' : ''}`, href: nav.repoCurrentBranchCommits()}, 'Commits')
),
m('li', {class: "nav-item"},
m('a', {class: `nav-link ${data.navTab === 'branches' ? 'active' : ''}`, href: nav.repoCurrentBranchBranches()}, 'Branches')
)
])
)
)
),
m('div', {class: "row"},
m('div', {class: "col-12"},
await pageContent
)
),
]
>>>>>>> main
}
}
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'
0 1 2 3 4 5
import m from 'mithril'
import render from 'mithril-node-render'
import { type Repository } from '../src/dataTypes.ts'
import getFlatRefs from '../src/flatRefs.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 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)),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 flatRef: ReturnType<typeof getFlatRefs>[0] = data.flatRef
const currentRepo: Repository = data.currentRepo
const currentRef: Repository['branches'][0] = data.currentRef
const currentRefCommits: Awaited<ReturnType<typeof flatPatches>> = data.currentRefCommits
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 ${flatRef.refName}`),
m.trust(`<link href="${ data.reposConfig.baseUrl + '/repos/' + slugify(flatRef.repoName) + `/${flatRef.type}/` + slugify(flatRef.refName) + '/commits.xml'}" rel="self" />`),
m.trust(`<link href="${data.reposConfig.baseUrl + '/repos/' + slugify(flatRef.repoName) + `/${flatRef.type}/` + slugify(flatRef.refName)}" />`),
m('updated', dateToRfc3339(currentRepo.commits.get(currentRef.sha).date)),
m('id', data.reposConfig.baseUrl),
m('author',
m('name', `${currentRepo.name} contributors`)
),
currentRefCommits.map((commit) => {
const commitUrl = data.reposConfig.baseUrl + '/repos/' + slugify(flatRef.repoName) + `/${flatRef.type}/` + slugify(flatRef.refName) + '/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 currentRel: Branch = data.currentRel
const fileInfo: FlatFileEntry = data.fileInfo
const pageContent = [ 14 15 16 17 18 19
const languageExtension = eleventyConfig.getFilter("languageExtension")
const renderContentIfAvailable = eleventyConfig.getFilter("renderContentIfAvailable")
const currentRef: Branch = data.currentRef
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(
currentRel.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(
currentRef.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(currentRel.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(currentRef.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(
currentRel.fileList.get(fileInfo.file).fileInfo.blameLines.map((annotation) => {
return `<a href="${data.reposPath}/${slugify(fileInfo.repoName)}/branch/${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.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(
currentRef.fileList.get(fileInfo.file).fileInfo.blameLines.map((annotation) => {
return `<a href="${data.reposPath}/${slugify(fileInfo.repoName)}/branch/${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(
currentRef.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.currentRel
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.currentRef
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.flatRel.relName)
])
)
]),13 14 15 16 17 18
m('div', {class: "col"},
m('p', [
'Files snapshot from ',
m('span', {class: "font-monospace"}, data.flatRef.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.flatRel.repoName)}/branch/${slugify(data.flatRel.relName)}/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.flatRef.repoName)}/branch/${slugify(data.flatRef.relName)}/files/${
file.fullPath.split('/')
.map((pathPart) => {
return pathPart.split('.').map((subPart) => {0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
import { type ReposConfiguration } from "../../src/configTypes.ts"
export const NavHelper = (reposConfig: ReposConfiguration, slugify: Function, repoName: string, branchName: string) => {
const reposPath = reposConfig.path || ""
// These two aren't actually used by any pages, but they're in almost
// every page URL. E.g. all of them start with 'repos/my-repo-name'
// or 'repos/my-repo-name/branches.'
const repoBasePath = `${reposPath}/${slugify(repoName)}`
const rootBasePathBranches = `${repoBasePath}/branch`
const rootBasePathTags = `${repoBasePath}/tag`
const currentBranchPath = `${rootBasePathBranches}/${slugify(branchName)}`
const repoBranchCommitsBase = `${currentBranchPath}/commits/`
return {
rootPath: () => {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
import { type ReposConfiguration } from "../../src/configTypes.ts"
/**
* Returns links to various pages used by the default template. Will return a link
* that assumes you want to stay on the current ref, unless you give a new ref in
* the function argument. E.g if you are currently at /tags/v-1-0/, and you call
* the commits() function, it'll give you /tag/v-1-0/commits, unless you pass in
* an argument like commits({refName: 'main', refType: 'branch'}), in which case
* it'll give you /branch/main/commits.
*/
type CurrentRefArgs = {
reposConfig: ReposConfiguration,
slugify: Function,
currentRepoName: string,
currentRefName: string,
currentRefType: 'branch' | 'tag'
}
export const NavHelper = (args: CurrentRefArgs) => {
const reposPath = args.reposConfig.path || ""
// These two aren't actually used by any pages, but they're in almost
// every page URL. E.g. all of them start with 'repos/my-repo-name'
// or 'repos/my-repo-name/branches.'
const repoBasePath = `${reposPath}/${args.slugify(args.currentRepoName)}`
const refPath = (refName, refType) => `${repoBasePath}/${refType}/${args.slugify(refName)}`
type RefArgument = {
refName: string,
refType: 'branch' | 'tag'
}
return {
rootPath: () => {22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
}
},
repoHomePath: () => {
return `${rootBasePathBranches}/${slugify(reposConfig.repos[repoName].defaultBranch)}`
},
repoCurrentBranchHome: () => {
return currentBranchPath
},
repoBranchHome: (branchName: string) => {
return `${rootBasePathBranches}/${slugify(branchName)}`
},
repoTagHome: (tagName: string) => {
return `${rootBasePathTags}/${slugify(tagName)}`
},
repoCurrentBranchFiles: () => {
return `${currentBranchPath}/files`
},
repoBranchCommitsBase: () => repoBranchCommitsBase,
repoCurrentBranchCommits: () => `${repoBranchCommitsBase}page1`,
repoCurrentBranchBranches: () => `${currentBranchPath}/branches`,
// TODO: change these "currentBranch"es to "currentRel"s
repoCurrentBranchTags: () => `${currentBranchPath}/tags`,
repoCurrentBranchRssFeed: () => `${currentBranchPath}/commits.xml`,
homepageButtons: reposConfig.repos[repoName].defaultTemplateConfiguration?.homepageButtons || []
}
}
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
}
},
repoHomePath: () => {
return repoBasePath
},
refHome: (newRef: RefArgument = null) => {
const refName = newRef?.refName || args.currentRefName
const refType = newRef?.refType || args.currentRefType
return (refType === 'branch' && refName === args.reposConfig.repos[args.currentRepoName].defaultBranch)
? repoBasePath
: refPath(refName, refType)
},
files: (newRef: RefArgument = null) => {
const refName = newRef?.refName || args.currentRefName
const refType = newRef?.refType || args.currentRefType
return `${refPath(refName, refType)}/files`
},
commit: (hash: string, newRef: RefArgument = null) => {
const refName = newRef?.refName || args.currentRefName
const refType = newRef?.refType || args.currentRefType
return `${refPath(refName, refType)}/commits/${hash}`
},
commits: (newRef: RefArgument = null) => {
const refName = newRef?.refName || args.currentRefName
const refType = newRef?.refType || args.currentRefType
return `${refPath(refName, refType)}/commits/page1`
},
branches: (newRef: RefArgument = null) => {
const refName = newRef?.refName || args.currentRefName
const refType = newRef?.refType || args.currentRefType
return `${refPath(refName, refType)}/branches`
},
tags: (newRef: RefArgument = null) => {
const refName = newRef?.refName || args.currentRefName
const refType = newRef?.refType || args.currentRefType
return `${refPath(refName, refType)}/tags`
},
rssFeed: (newRef: RefArgument = null) => {
const refName = newRef?.refName || args.currentRefName
const refType = newRef?.refType || args.currentRefType
return `${refPath(refName, refType)}/commits.xml`
},
homepageButtons: args.reposConfig.repos[args.currentRepoName].defaultTemplateConfiguration?.homepageButtons || []
}
}
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
}2 3 4 5 6 7
export default async (eleventyConfig: any) => {
return async (data) => {
const branch: Branch = data.currentRef
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 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 files4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
export default async (reposConfig: any, eleventyConfig: any, data: any) => {
const repo: Repository = data.currentRepo
const ref: Repository['branches'][0] | Repository['tags'][0] = data.currentRef
const renderContentIfAvailable = eleventyConfig.getFilter("renderContentIfAvailable")
const slugify = eleventyConfig.getFilter("slugify")
const getReadMe = eleventyConfig.getFilter("getReadMe")
const latestCommit = repo.commits.get(ref.sha)
const latestCommitMessage = latestCommit.message.length > 72
? latestCommit.message.split('\n')[0].substr(0, 72) + '...'
: latestCommit.message
const nav = NavHelper({
reposConfig,
slugify,
currentRepoName: repo.name,
currentRefName: data.flatRef.name,
currentRefType: data.flatRef.type,
})
const languageCounts = Array.from(ref.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(rel.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(ref.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, rel.name), rel.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, ref.name), ref.name)
const pageContent = [
m('div', {class: "row"}, [102 103 104 105 106 107 108 109 110 111 112
m('div', {class: "row mt-2"}, [
m('div', {class: "col"}, [
m('p', {class: "font-monospace text-white mb-2 d-inline-block"}, [
m('a', {class: "btn btn-outline-info badge shadow-none me-2", href: nav.repoCurrentBranchRssFeed()}, [
m('img', {src: `${nav.rootPath()}frontend/img/rss-icon.svg`, style: "width: 11px; margin-right: 5px;"}),
'RSS'
]),
`Latest commit: ${latestCommit.date.toDateString()} `,
m('a', {class: "fw-bold link-info", href: `${nav.repoBranchCommitsBase()}${latestCommit.hash}`}, latestCommit.hash.substr(0, 6)),
' ',
latestCommitMessage,
])102 103 104 105 106 107 108 109 110 111 112
m('div', {class: "row mt-2"}, [
m('div', {class: "col"}, [
m('p', {class: "font-monospace text-white mb-2 d-inline-block"}, [
m('a', {class: "btn btn-outline-info badge shadow-none me-2", href: nav.rssFeed()}, [
m('img', {src: `${nav.rootPath()}frontend/img/rss-icon.svg`, style: "width: 11px; margin-right: 5px;"}),
'RSS'
]),
`Latest commit: ${latestCommit.date.toDateString()} `,
m('a', {class: "fw-bold link-info", href: `${nav.commit(latestCommit.hash)}`}, latestCommit.hash.substr(0, 6)),
' ',
latestCommitMessage,
])7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
data: any,
) => {
const slugify = eleventyConfig.getFilter("slugify")
const nav = NavHelper(reposConfig, slugify, data.flatRel.repoName, data.flatRel.relName)
const tags = data.flatRels.filter(rel => rel.type === "tag")
const branchCards = (tags) => {
return m('ul', tags.map((tag) => {
return tag.repoName === data.flatRel.repoName ?
m('li', [
m('a', {
href: `${data.reposPath}/${slugify(tag.repoName)}/tag/${slugify(tag.relName)}/tags`
}, tag.relName),
tag.relName === data.flatRel.relName
? m('div', {class: "badge rounded-pill bg-secondary mx-1"}, 'current') : null,
m('ul', [
tag.description ? m('li', tag.description) : null7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
data: any,
) => {
const slugify = eleventyConfig.getFilter("slugify")
const nav = NavHelper({
reposConfig,
slugify,
currentRepoName: data.flatRef.repoName,
currentRefName: data.flatRef.refName,
currentRefType: data.flatRef.refType,
})
const tags = data.flatRefs.filter(ref => ref.type === "tag")
const branchCards = (tags) => {
return m('ul', tags.map((tag) => {
return tag.repoName === data.flatRef.repoName ?
m('li', [
m('a', {
href: `${data.reposPath}/${slugify(tag.repoName)}/tag/${slugify(tag.refName)}/tags`
}, tag.refName),
tag.refName === data.flatRef.refName
? m('div', {class: "badge rounded-pill bg-secondary mx-1"}, 'current') : null,
m('ul', [
tag.description ? m('li', tag.description) : null1 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'1 2 3 4 5 6
import util from 'util'
import childProcess from 'child_process'
import {repos, getBranchesAndTags} from './src/repos.ts'
import getFlatRefs from './src/flatRefs.ts'
import flatFiles from './src/flatFiles.ts'
import flatPatches from './src/flatPatches.ts'
import paginatedPatches, {type PatchPage} from './src/paginatedPatches.ts'88 89 90 91 92 93 94
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("/")88 89 90 91 92 93 94
eleventyConfig.addGlobalData("reposConfig", reposConfiguration)
eleventyConfig.addGlobalData("reposPath", reposPath)
const flatRefs = getFlatRefs(reposData)
eleventyConfig.addGlobalData("flatRefs", flatRefs)
eleventyConfig.addFilter("getFileName", (filePath: string) => {
const pathParts = filePath.split("/")253 254 255 256 257 258 259
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) => {253 254 255 256 257 258 259
return isDirectory
})
eleventyConfig.addFilter("pagesJustForRef", (pages: Array<PatchPage>, repoName: string, refName: string, refType: "branch" | "tag") => {
return pages.filter(page => page.repoName === repoName && page.refName === refName && page.type === refType)
})
eleventyConfig.addFilter("date", (dateString: string) => {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 327
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",
}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 327 328
commonPage(branchesJsTemplate, reposConfiguration, eleventyConfig),
{
pagination: {
data: "flatRefs",
size: 1,
alias: "flatRef",
},
permalink: (data) => {
const repoName = data.flatRef.repoName
const refName = data.flatRef.refName
const refType = data.flatRef.type
return `${reposPath}/${eleventyConfig.getFilter("slugify")(repoName)}/${refType}/${eleventyConfig.getFilter("slugify")(refName)}/branches/`
},
eleventyComputed: {
nav: {
repoName: (data) => data.flatRef.repoName,
refName: (data) => data.flatRef.refName,
path: "branches"
},
currentRepo: (data) => reposData.find(repo => {
return repo.name === data.flatRef.repoName
}),
currentRef: (data) => {
const refType = data.flatRef.type === "branch" ? "branches" : "tags"
const currentRepo = reposData.find(repo => {
return repo.name === data.flatRef.repoName
})
return currentRepo[refType].find(ref => {
return ref.name === data.flatRef.refName
})
},
currentRefType: (data) => data.flatRef.type,
},
navTab: "branches",
}334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
commonPage(tagsJsTemplate, 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)}/tags/`
},
eleventyComputed: {
nav: {
repoName: (data) => data.flatRel.repoName,
relName: (data) => data.flatRel.relName,
path: "tags"
},
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: "tags",
}334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367
commonPage(tagsJsTemplate, reposConfiguration, eleventyConfig),
{
pagination: {
data: "flatRefs",
size: 1,
alias: "flatRef",
},
permalink: (data) => {
const repoName = data.flatRef.repoName
const refName = data.flatRef.refName
const refType = data.flatRef.type
return `${reposPath}/${eleventyConfig.getFilter("slugify")(repoName)}/${refType}/${eleventyConfig.getFilter("slugify")(refName)}/tags/`
},
eleventyComputed: {
nav: {
repoName: (data) => data.flatRef.repoName,
refName: (data) => data.flatRef.refName,
path: "tags"
},
currentRepo: (data) => reposData.find(repo => {
return repo.name === data.flatRef.repoName
}),
currentRef: (data) => {
const refType = data.flatRef.type === "branch" ? "branches" : "tags"
const currentRepo = reposData.find(repo => {
return repo.name === data.flatRef.repoName
})
return currentRepo[refType].find(ref => {
return ref.name === data.flatRef.refName
})
},
currentRefType: (data) => data.flatRef.type,
},
navTab: "tags",
}382 383 384 385 386 387 388
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: {382 383 384 385 386 387 388
permalink: (data) => {
const repoName = data.fileInfo.repoName
const branchName = data.fileInfo.branchName
const refType = data.fileInfo.type
return `${reposPath}/${eleventyConfig.getFilter("slugify")(repoName)}/${refType}/${eleventyConfig.getFilter("slugify")(branchName)}/files/${data.fileInfo.file.split('/').map((filePart) => filePart.split('.').map((subPart) => eleventyConfig.getFilter("slugify")(subPart)).join('.')).join('/')}.html`
},
eleventyComputed: {
nav: {394 395 396 397 398 399 400 401 402 403
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.branchName
}),
},
navTab: "files",
}394 395 396 397 398 399 400 401 402 403 404
currentRepo: (data) => reposData.find(repo => {
return repo.name === data.fileInfo.repoName
}),
currentRef: (data) => reposData.find(repo => {
return repo.name === data.fileInfo.repoName
}).branches.find(branch => {
return branch.name === data.fileInfo.branchName
}),
currentRefType: (data) => data.fileInfo.refType,
},
navTab: "files",
}419 420 421 422 423 424 425 426 427 428 429 430 431 432
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.branchName
}),
}
}
)419 420 421 422 423 424 425 426 427 428 429 430 431 432 433
permalink: (data) => {
const repoName = data.fileInfo.repoName
const branchName = data.fileInfo.branchName
const refType = data.fileInfo.type
return `${reposPath}/${eleventyConfig.getFilter("slugify")(repoName)}/${refType}/${eleventyConfig.getFilter("slugify")(branchName)}/raw/${data.fileInfo.file.split('.').map(filePart => eleventyConfig.getFilter("slugify")(filePart)).join('.')}`
},
eleventyComputed: {
currentRef: (data) => reposData.find(repo => {
return repo.name === data.fileInfo.repoName
}).branches.find(branch => {
return branch.name === data.fileInfo.branchName
}),
currentRefType: (data) => data.fileInfo.refType,
}
}
)438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469
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",
}438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470
commonPage(filesJsTemplate, reposConfiguration, eleventyConfig),
{
pagination: {
data: "flatRefs",
size: 1,
alias: "flatRef",
},
permalink: (data) => {
const repoName = data.flatRef.repoName
const refName = data.flatRef.refName
const refType = data.flatRef.type
return `${reposPath}/${eleventyConfig.getFilter("slugify")(repoName)}/${refType}/${eleventyConfig.getFilter("slugify")(refName)}/files/`
},
eleventyComputed: {
nav: {
repoName: (data) => data.flatRef.repoName,
refName: (data) => data.flatRef.refName,
path: "files",
},
currentRepo: (data) => reposData.find(repo => {
return repo.name === data.flatRef.repoName
}),
currentRef: (data) => {
const refType = data.flatRef.type === "branch" ? "branches" : "tags"
return reposData.find(repo => {
return repo.name === data.flatRef.repoName
})[refType].find(ref => {
return ref.name === data.flatRef.refName
})
},
currentRefType: (data) => data.flatRef.type,
},
navTab: "files",
}476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507
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"
}476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513
commonPage(repoJsTemplate, reposConfiguration, eleventyConfig),
{
pagination: {
data: "flatRefs",
size: 1,
alias: "flatRef",
},
permalink: (data) => {
const repoName = data.flatRef.repoName
const refName = data.flatRef.refName
const refType = data.flatRef.type
if (refType === "branch" && refName === reposConfiguration.repos[repoName].defaultBranch) {
return `${reposPath}/${eleventyConfig.getFilter("slugify")(repoName)}/`
}
else {
return `${reposPath}/${eleventyConfig.getFilter("slugify")(repoName)}/${refType}/${eleventyConfig.getFilter("slugify")(refName)}/`
}
},
eleventyComputed: {
nav: {
repoName: (data) => data.flatRef.repoName,
refName: (data) => data.flatRef.refName,
path: (data) => '', // TODO: ask about why empty string here shows up as a function
},
currentRepo: (data) => reposData.find(repo => {
return repo.name === data.flatRef.repoName
}),
currentRef: (data) => {
const refType = data.flatRef.type === "branch" ? "branches" : "tags"
return reposData.find(repo => {
return repo.name === data.flatRef.repoName
})[refType].find(ref => {
return ref.name === data.flatRef.refName
})
},
currentRefType: (data) => data.flatRef.type
},
navTab: "home"
}522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547
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",
}522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548
paginatedPatches: paginatedPatchesData,
permalink: (data) => {
const repoName = data.patchPage.repoName
const refName = data.patchPage.refName
const refType = data.patchPage.type
return `${reposPath}/${eleventyConfig.getFilter("slugify")(repoName)}/${refType}/${eleventyConfig.getFilter("slugify")(refName)}/commits/page${data.patchPage.pageNumber}/`
},
eleventyComputed: {
nav: {
repoName: (data) => data.patchPage.repoName,
refName: (data) => data.patchPage.refName,
path: 'commits/page1',
},
currentRepo: (data) => reposData.find(repo => {
return repo.name === data.patchPage.repoName
}),
currentRef: (data) => {
const refType = data.patchPage.type === "branch" ? "branches" : "tags"
return reposData.find(repo => {
return repo.name === data.patchPage.repoName
})[refType].find(ref => {
return ref.name === data.patchPage.refName
})
},
currentRefType: (data) => data.patchPage.type,
},
navTab: "commits",
}562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587
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",
}562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588
flatPatches: flatPatchesData,
permalink: (data) => {
const repoName = data.patchInfo.repoName
const refName = data.patchInfo.refName
const refType = data.patchInfo.type
return `${reposPath}/${eleventyConfig.getFilter("slugify")(repoName)}/${refType}/${eleventyConfig.getFilter("slugify")(refName)}/commits/${data.patchInfo.commit.hash}/`
},
eleventyComputed: {
nav: {
repoName: (data) => data.patchInfo.repoName,
refName: (data) => data.patchInfo.refName,
path: 'commits/page1',
},
currentRepo: (data) => reposData.find(repo => {
return repo.name === data.patchInfo.repoName
}),
currentRef: (data) => {
const refType = data.patchInfo.type === "branch" ? "branches" : "tags"
return reposData.find(repo => {
return repo.name === data.patchInfo.repoName
})[refType].find(ref => {
return ref.name === data.patchInfo.refName
})
},
currentRefType: (data) => data.patchInfo.type
},
navTab: "commits",
}594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623
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
})
}
},594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624
feedJsTemplate(eleventyConfig),
{
pagination: {
data: "flatRefs",
size: 1,
alias: "flatRef",
},
permalink: (data) => {
const repoName = data.flatRef.repoName
const refName = data.flatRef.refName
const refType = data.flatRef.type
return `${reposPath}/${eleventyConfig.getFilter("slugify")(repoName)}/${refType}/${eleventyConfig.getFilter("slugify")(refName)}/commits.xml`
},
eleventyComputed: {
currentRepo: (data) => reposData.find(repo => {
return repo.name === data.flatRef.repoName
}),
currentRef: (data) => {
const refType = data.flatRef.type === "branch" ? "branches" : "tags"
return reposData.find(repo => {
return repo.name === data.flatRef.repoName
})[refType].find(ref => {
return ref.name === data.flatRef.refName
})
},
currentRefType: (data) => data.flatRef.type,
currentRefCommits: (data) => {
return flatPatchesData.filter((patch) => {
return patch.refName === data.flatRef.refName
})
}
},
-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
const singleCommit = `
commit 0d861762014e3ae60bca789653df9e3698d3eff3
Author: Tucker McKnight <tmcknight@instructure.com>
Date: Sat Mar 14 13:24:39 2026 -0600
third commit
diff --git a/README.md b/README.md
index deef711..fb2b345 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,3 @@
-initial commit text
+initial commit inline change text
+third commit
second commit
`
const largerCommit = `
commit b517d8290796b45d05de989e438508efe03be324
Author: Tucker McKnight <tmcknight@instructure.com>
Date: Sat Mar 7 19:57:20 2026 -0700
add colors to language bar chart
diff --git a/new-commits-page.html b/new-commits-page.html
index 03aff8f..5e85d9b 100644
--- a/new-commits-page.html
+++ b/new-commits-page.html
@@ -10,6 +10,8 @@
<div class="container commits-page">
<div class="row m-2">
<div class="col">
+
+ <!-- A COMMIT -->
<div class="row commit">
<div class="col">
<div class="row">
`
export {
singleCommit,
largerCommit,
}
-1 0 1 2 3 4 5 6
import { expect, test } from 'vitest'
import { getGitDiffsFromPatchText } from '../../dist/src/helpers.js'
import { singleCommit, largerCommit } from './fixtures/patches.js'
test("asdf", () => {
const result = getGitDiffsFromPatchText(largerCommit)
console.log(result[0])
expect(true).toBe(true)
})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
export type Repository = {
name: string,
description?: string,
cloneUrl: string,
defaultBranch: string,
branches: Array<{
name: string,
head: string,
fileList: Array<string>,
}>,
tags?: Array<{
name: string,
sha: string,
fileList: Array<string>,
}>,
<<<<<<< HEAD
commits: Map<string, {
hash: string,
=======
commits?: Map<string, {
>>>>>>> 359136b (allow commit references to be null, e.g. for the first commit which has no parent)
message: string,
author: string,
date: Date,
parent: string | null,
diffs: Array<{
fileName: string,
lineNumber: number,
beforeText: string,
afterText: string,
}>
}>,
}
2 3 4 5 6 7
type FlatPatchRecord = {
commit: ReturnType<Repository['commits']['get']>,
repoName: string,
relName: string,
type: "branch" | "tag",
}
2 3 4 5 6 7
type FlatPatchRecord = {
commit: ReturnType<Repository['commits']['get']>,
repoName: string,
refName: string,
type: "branch" | "tag",
}
22 23 24 25 26 27
type: "branch",
commit: currentCommit,
repoName: repo.name,
relName: branch.name
})
currentCommit = repo.commits.get(currentCommit.parent)22 23 24 25 26 27
type: "branch",
commit: currentCommit,
repoName: repo.name,
refName: branch.name
})
currentCommit = repo.commits.get(currentCommit.parent)39 40 41 42 43 44
type: "tag",
commit: currentCommit,
repoName: repo.name,
relName: tag.name,
})
currentCommit = repo.commits.get(currentCommit.parent)39 40 41 42 43 44
type: "tag",
commit: currentCommit,
repoName: repo.name,
refName: tag.name,
})
currentCommit = repo.commits.get(currentCommit.parent)0 1 2 3 4 5
import { type Repository } from "./dataTypes.ts"
type BranchEntry = {
relName: string,
type: "branch",
repoName: string,
compareTo: string,0 1 2 3 4 5
import { type Repository } from "./dataTypes.ts"
type BranchEntry = {
refName: string,
type: "branch",
repoName: string,
compareTo: string,9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
}
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,9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
}
type TagEntry = {
refName: string,
type: "tag",
repoName: string,
}
let cachedRefs: Array<BranchEntry | TagEntry> | null = null
export default (repos: Array<Repository>) => {
if (cachedRefs !== null) { return cachedRefs }
cachedRefs = repos.flatMap((repo) => {
const branches: Array<BranchEntry> = repo.branches.map((branch) => {
const result: BranchEntry = {
refName: branch.name,
type: "branch",
repoName: repo.name,
compareTo: branch.compareTo,34 35 36 37 38 39
})
const tags: Array<TagEntry> = repo.tags.map((tag) => {
return {
relName: tag.name,
type: "tag",
repoName: repo.name,
}34 35 36 37 38 39
})
const tags: Array<TagEntry> = repo.tags.map((tag) => {
return {
refName: tag.name,
type: "tag",
repoName: repo.name,
}43 44 45 46
return [...branches, ...tags]
})
return cachedRels
}43 44 45 46
return [...branches, ...tags]
})
return cachedRefs
}2 3 4 5 6 7
export type PatchPage = {
repoName: string,
relName: string,
type: "branch" | "tag",
commits: Array<ReturnType<Repository['commits']['get']>>,
pageNumber: number,2 3 4 5 6 7
export type PatchPage = {
repoName: string,
refName: string,
type: "branch" | "tag",
commits: Array<ReturnType<Repository['commits']['get']>>,
pageNumber: number,21 22 23 24 25 26
const index = paginatedPatches.findIndex((page) => {
return (
page.repoName === patch.repoName
&& page.relName == patch.relName
&& page.type == patch.type
&& page.commits.length <= patchesPerPage
)21 22 23 24 25 26
const index = paginatedPatches.findIndex((page) => {
return (
page.repoName === patch.repoName
&& page.refName == patch.refName
&& page.type == patch.type
&& page.commits.length <= patchesPerPage
)30 31 32 33 34 35 36 37 38 39 40
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 already30 31 32 33 34 35 36 37 38 39 40
if (index === -1) {
const pageNumber = paginatedPatches.filter(page => (
page.repoName === patch.repoName
&& page.refName === patch.refName
&& page.type === patch.type
)).length + 1
paginatedPatches.push({
repoName: patch.repoName,
refName: patch.refName,
commits: [patch.commit],
type: patch.type,
// current page number is one more than "how many items in paginatedPatches already26 27 28 29 30 31
const cachedBranchNames = branchesForReposMap.get(repoName)
const cachedTagNames = tagsForReposMap.get(repoName)
if (cachedBranchNames !== undefined) {
console.log({branches: cachedBranchNames, tags: cachedTagNames})
return {branches: cachedBranchNames, tags: cachedTagNames}
}
26 27 28 29 30
const cachedBranchNames = branchesForReposMap.get(repoName)
const cachedTagNames = tagsForReposMap.get(repoName)
if (cachedBranchNames !== undefined) {
return {branches: cachedBranchNames, tags: cachedTagNames}
}
127 128 129 130 131 132 133
}).flat()
}).flat()
console.log(tags)
if (branchesForReposMap.get(repoName) === undefined) {
branchesForReposMap.set(repoName, branches)
}127 128 129 130 131
}).flat()
}).flat()
if (branchesForReposMap.get(repoName) === undefined) {
branchesForReposMap.set(repoName, branches)
}