Tucker McKnight <tucker@pangolin.lan> | Sun Feb 15 2026
[WIP] things broken here, getting types correct for file caching Adds a new fileList map underneath a branch. fileList entries point to a fileInfo object, which should eventually reference the cached file contents underneath the commits.cachedFiles object. (It's an object so that we get a reference to it, rather than copying the whole value.) Also adds a new blameLines entry so we can save those in the object, too. Currently this is just using placeholder, empty arrays, and does not actually contain the contents of the files. Every file page is blank, as is the git blame info.
1 2
const getFileContents = eleventyConfig.getFilter("getFileContents")
const getFileLastTouchInfo = eleventyConfig.getFilter("getFileLastTouchInfo")1 2
import { type Repository } from '../src/dataTypes.ts'
import { type FlatFileEntry } from '../src/flatFiles.ts'
type Branch = Repository['branches'][0]14
m('span', {class: "font-monospace"}, data.fileInfo.branchName)14
const currentBranch: Branch = data.currentBranch
const fileInfo: FlatFileEntry = data.fileInfo
m('span', {class: "font-monospace"}, fileInfo.branchName)27 28 29 30 31 32 33
m('a', {href: `${data.reposPath}/${data.fileInfo.repoName}/branches/${data.fileInfo.branchName}/files`}, './')
data.fileInfo.file.split('/').map((dir, index, arr) => {
href: `${data.reposPath}/${data.fileInfo.repoName}/branches/${data.fileInfo.branchName}/files/${arr.slice(0, index + 1).map((part) => slugify(part)).join('/')}.html`}, `${dir}/`)
(isDirectory(data.fileInfo.file, data.fileInfo.repoName, data.fileInfo.branchName) ?
topLevelFilesOnly(getDirectoryContents(data.fileInfo.repoName, data.fileInfo.branchName, data.fileInfo.file), data.fileInfo.file + '/').map((dir) => {
href: `${data.reposPath}/${slugify(data.fileInfo.repoName)}/branches/${slugify(data.fileInfo.branchName)}/files/${dir.fullPath.split('/').map((pathPart) => {
getRelativePath(data.fileInfo.file, dir.name)27 28 29 30 31 32 33
m('a', {href: `${data.reposPath}/${fileInfo.repoName}/branches/${fileInfo.branchName}/files`}, './')
fileInfo.file.split('/').map((dir, index, arr) => {
href: `${data.reposPath}/${fileInfo.repoName}/branches/${fileInfo.branchName}/files/${arr.slice(0, index + 1).map((part) => slugify(part)).join('/')}.html`}, `${dir}/`)
(isDirectory(fileInfo.file, fileInfo.repoName, fileInfo.branchName) ?
topLevelFilesOnly(getDirectoryContents(fileInfo.repoName, fileInfo.branchName, fileInfo.file), fileInfo.file + '/').map((dir) => {
href: `${data.reposPath}/${slugify(fileInfo.repoName)}/branches/${slugify(fileInfo.branchName)}/files/${dir.fullPath.split('/').map((pathPart) => {
getRelativePath(fileInfo.file, dir.name)67 68
href: `${data.reposPath}/${slugify(data.fileInfo.repoName)}/branches/${slugify(data.fileInfo.branchName)}/raw/${data.fileInfo.file.split('.').map(filePart => slugify(filePart)).join('.')}`}, 'View raw file'))
(data.fileInfo.file.endsWith(".md") ?67 68
href: `${data.reposPath}/${slugify(fileInfo.repoName)}/branches/${slugify(fileInfo.branchName)}/raw/${fileInfo.file.split('.').map(filePart => slugify(filePart)).join('.')}`}, 'View raw file'))
(fileInfo.file.endsWith(".md") ?91 92 93 94 95 96 97
m.trust(await renderContentIfAvailable(await getFileContents(
data.fileInfo.repoName,
data.fileInfo.branchName,
data.fileInfo.file
), data.fileInfo.branchName)
))
m('div', {class: `row code-content ${data.fileInfo.file.endsWith('.md') ? 'd-none' : ''}`},91 92 93 94 95 96 97
m.trust(await renderContentIfAvailable(
currentBranch.fileList.get(fileInfo.file).fileInfo.contents
))
)
m('div', {class: `row code-content ${fileInfo.file.endsWith('.md') ? 'd-none' : ''}`},114
lineNumbers(await getFileContents(data.fileInfo.repoName, data.fileInfo.branchName, data.fileInfo.file)).map((lineNumber) => {114
lineNumbers(currentBranch.fileList.get(fileInfo.file).fileInfo.contents).map((lineNumber) => {123 124 125 126 127 128 129 130 131 132 133 134 135
(await getFileLastTouchInfo(
data.fileInfo.repoName,
data.fileInfo.branchName,
data.fileInfo.file
)).map((annotation) => {
return `<a href="${data.reposPath}/${slugify(data.fileInfo.repoName)}/branches/${slugify(data.fileInfo.branchName)}/commits/${annotation.sha}">${annotation.sha.substr(0, 6)}</a> ${annotation.author}`
await getFileContents(
data.fileInfo.repoName,
data.fileInfo.branchName,
data.fileInfo.file
),
data.fileInfo.file,
data.fileInfo.repoName123 124 125 126 127 128 129 130 131 132 133 134 135
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}`
currentBranch.fileList.get(fileInfo.file).fileInfo.contents,
fileInfo.file,
fileInfo.repoName7
const files: SortedFileList = topLevelFilesOnly(branch.fileList, '')7
const files: SortedFileList = topLevelFilesOnly(Array.from(branch.fileList.keys()), '')1 2 3
export default async (eleventyConfig: any) => {
const getFileContents = eleventyConfig.getFilter("getFileContents")
return await getFileContents(data.fileInfo.repoName, data.fileInfo.branchName, data.fileInfo.file)1 2 3
import { type Repository } from "../src/dataTypes.ts"
type Branch = Repository['branches'][0]
export default async (eleventyConfig: any) => {
const branch: Branch = data.currentBranch
return branch.fileList.get(data.fileInfo.file).fileInfo.contents13
const languageCounts = branch.fileList.reduce((counts, currentFile) => {13
const languageCounts = Array.from(branch.fileList.keys()).reduce((counts, currentFile) => {28
const total = branch.fileList.length28
const total = Array.from(branch.fileList.keys()).length7
import * as operations from './src/vcses/git/operations.ts'7
128
return reposData.find(current => current.name === repo).branches.find(current => current.name === branch).fileList.filter(file => file.startsWith(dirPath) && file !== dirPath)128
const fileList = reposData.find(
current => current.name === repo
).branches.find(
current => current.name === branch
).fileList
return Array.from(fileList.keys()).filter(
file => file.startsWith(dirPath) && file !== dirPath
)215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
eleventyConfig.addAsyncFilter("getFileLastTouchInfo", async (repo: string, branch: string, filename: string) => {
const ignoreExtensions = ['.png', '.jpg']
if (ignoreExtensions.some(extension => filename.endsWith(extension))) {
return ""
}
const location = getLocation(reposConfiguration, eleventyConfig.dir.output, repo)
return operations.getFileLastTouchInfo(branch, filename, location)
})
const isDirectory = files.some((testFile) => {
eleventyConfig.addAsyncFilter("getFileContents", async (repo: string, branch: string, filename: string) => {
const location = getLocation(reposConfiguration, eleventyConfig.dir.output, repo)
const command = `git -C ${location} show ${branch}:${filename}`
const res = await exec(command)
return res.stdout
})
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
const isDirectory = Array.from(files.keys()).some((testFile) => {363
363
eleventyComputed: {
currentBranch: (data) => reposData.find(repo => {
return repo.name === data.fileInfo.repoName
}).branches.find(branch => {
return branch.name === data.fileInfo.branchName
}),
}1
1
import { type BlameInfo } from "./vcses/git/operations.ts"
export type FileInfo = {
contents: string,
lastModified: Date,
blameLines: Array<BlameInfo>,
}
10 11
fileList: Array<string>,
fileList: Array<string>,10 11
// 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 mentions
// this filename. If one is found, return the *object* in the cached file
// map (so that it returns a reference). Also set that in a const inside
// the function so that we don't have to compute it again.
// If one is not found, then `cat` the file and save it in the commit
// cache.
fileList: Map<string, {fileInfo: FileInfo}>,
fileList: Map<string, {fileInfo: FileInfo}>,
// TODO: put cached files map in here. Save file as an object
// so that it gets saved by reference.
cachedFiles: Map<string, FileInfo>,1 2 3
let cachedFlatFiles = null
export default (repos: Array<Repository>) => {
return branch.fileList.map((file) => {1 2 3
let cachedFlatFiles: Array<FlatFileEntry> | null = null
export type FlatFileEntry = {
file: string,
branchName: string,
repoName: string,
}
export default (repos: Array<Repository>) : Array<FlatFileEntry> => {
return Array.from(branch.fileList.keys()).map((file) => {164
tags: [], // todo164
tags: [], // todo fill in tags, similar to branches2 3
import { type Repository } from '../../dataTypes.ts'
export const getFileList = async (branchName: string, repoLocation: string) => {2 3
import { type Repository, type FileInfo } from '../../dataTypes.ts'
export const getFileList = async (
branchName: string, repoLocation: string
) : Promise<Map<string, {fileInfo: FileInfo}>> => {20
const fileSet: Set<string> = new Set()20
const filesMap: Map<string, {fileInfo: FileInfo}> = new Map()32 33
fileSet.add(path)
return Array.from(fileSet)32 33
filesMap.set(path, {
get fileInfo() {
return {
contents: "",
lastModified: new Date(),
blameLines: []
}
}
})
return filesMap105
export const getFileLastTouchInfo = async (branch: string, filename: string, repoLocation: string) => {105
cachedFiles: new Map(),
export type BlameInfo = {
sha: string,
author: string,
}
export const getFileLastTouchInfo = async (
branch: string, filename: string, repoLocation: string
) : Promise<Array<BlameInfo>> => {132
let authorsAndShasByLine = []132
let authorsAndShasByLine: Array<BlameInfo> = []