Tucker McKnight <tmcknight@instructure.com> | Tue May 26 2026
WIP: placeholder for file content and blame lines
15 16 17 18 19
const renderContentIfAvailable = eleventyConfig.getFilter("renderContentIfAvailable")
const currentRef: Branch = data.currentRef
const fileInfo: FlatFileEntry = data.fileInfo
const pageContent = [ 15 16 17 18 19 20
const renderContentIfAvailable = eleventyConfig.getFilter("renderContentIfAvailable")
const currentRef: Branch = data.currentRef
const currentRepo: Repository = data.currentRepo
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(
currentRef.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(
(await currentRepo.files(fileInfo.file, currentRef.sha)).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(currentRef.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((await currentRepo.files(fileInfo.file, currentRef.sha)).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(
currentRef.fileList.get(fileInfo.file).fileInfo.blameLines.map((annotation) => {
return `<a href="${data.reposPath}/${slugify(fileInfo.repoName)}/${fileInfo.type}/${slugify(fileInfo.refName)}/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.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(
(await currentRepo.files(fileInfo.file, currentRef.sha)).blameLines.map((annotation) => {
return `<a href="${data.reposPath}/${slugify(fileInfo.repoName)}/${fileInfo.type}/${slugify(fileInfo.refName)}/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(
(await currentRepo.files(fileInfo.file, currentRef.sha)).contents,
languageExtension(
fileInfo.file,
fileInfo.repoName0 1 2 3 4 5 6 7 8
import { type Repository } from "../src/dataTypes.ts"
type Branch = Repository['branches'][0]
export default async (eleventyConfig: any) => {
return async (data) => {
const branch: Branch = data.currentRef
return branch.fileList.get(data.fileInfo.file).fileInfo.contents
}
}0 1 2 3 4 5 6 7 8 9
import { type Repository } from "../src/dataTypes.ts"
type Branch = Repository['branches'][0]
type Tag = Repository['tags'][0]
export default async (eleventyConfig: any) => {
return async (data) => {
const branch: Branch | Tag = data.currentRef
return (await data.currentRepo.files(data.fileInfo.file, branch.sha)).contents
}
}435 436 437 438 439
return `${reposPath}/${eleventyConfig.getFilter("slugify")(repoName)}/${refType}/${eleventyConfig.getFilter("slugify")(refName)}/raw/${data.fileInfo.file.split('.').map(filePart => eleventyConfig.getFilter("slugify")(filePart)).join('.')}`
},
eleventyComputed: {
currentRef: (data) => {
const refKey = data.fileInfo.type === 'branch' ? 'branches' : 'tags'
435 436 437 438 439 440 441 442
return `${reposPath}/${eleventyConfig.getFilter("slugify")(repoName)}/${refType}/${eleventyConfig.getFilter("slugify")(refName)}/raw/${data.fileInfo.file.split('.').map(filePart => eleventyConfig.getFilter("slugify")(filePart)).join('.')}`
},
eleventyComputed: {
currentRepo: (data) => reposData.find(repo => {
return repo.name === data.fileInfo.repoName
}),
currentRef: (data) => {
const refKey = data.fileInfo.type === 'branch' ? 'branches' : 'tags'
7 8 9 10 11 12 13 14
export type Commit = {
hash: string,
// TODO: put cached files map in here. Save file as an object
// so that it gets saved by reference.
cachedFiles: Map<string, FileInfo>,
message: string,
isMerge: boolean,
author: string,7 8 9 10 11
export type Commit = {
hash: string,
message: string,
isMerge: boolean,
author: string,35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
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 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.
// The FileInfo needs to know which commit it is looking at, then, in order
// to query for the file contents or to search around the existing file list
// to see if a copy is already available.
fileList: Map<string, {fileInfo: FileInfo}>,
}>,
tags?: Array<{
name: string,
sha: string,
fileList: Map<string, {fileInfo: FileInfo}>,
}>,
commits?: Map<string, Commit>,
}35 36 37 38 39 40 41 42 43 44 45 46
behind: number,
compareTo: string,
sha: string,
fileList: Set<string>,
}>,
files: (filename: string, sha: string) => Promise<FileInfo>,
tags?: Array<{
name: string,
sha: string,
fileList: Set<string>,
}>,
commits?: Map<string, Commit>,
}224 225 226 227 228
cloneUrl: cloneUrl.cloneUrl(reposConfig.baseUrl, repoName),
defaultBranch: reposConfig.repos[repoName].defaultBranch,
tags: tags,
commits,
})
}224 225 226 227 228 229 230 231 232 233 234 235 236 237
cloneUrl: cloneUrl.cloneUrl(reposConfig.baseUrl, repoName),
defaultBranch: reposConfig.repos[repoName].defaultBranch,
tags: tags,
files: async (filename: string, sha: string) => {
return Promise.resolve({
contents: "Pretend like this is the text of the file.",
lastModified: new Date(),
blameLines: [
{ sha: 'abc123', author: 'You Did This' }
]
})
},
commits,
})
}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
import childProcess from 'child_process'
const exec = util.promisify(childProcess.exec)
import { getGitDiffsFromPatchText} from '../../helpers.ts'
import { type Repository, type FileInfo } from '../../dataTypes.ts'
export const getFileList = async (
branchName: string, repoLocation: string
) : Promise<Map<string, {fileInfo: FileInfo}>> => {
const command = `git -C ${repoLocation} ls-tree -r --name-only ${branchName}`
const result = await exec(command)
let files = result.stdout.split("\n").filter(item => item.length > 0 && item != ".")
// TODO: this could be better. This is adding each sub-path of a file to a set, so that
// we don't wind up with repeats, and then converting that back into an array. E.g. if
// we have two files:
// - posts/blog/one.md
// - posts/blog/two.md
// this will add the following to the set:
// posts, posts/blog, posts/blog/one.md, posts, posts/blog, posts/blog/two.md
// The repeats will be omitted because it's a Set, and the resulting array will
// be [posts, posts/blog, posts/blog/one.md, posts/blog/two.md].
// This is because it's convenient to have the directories show up as their own "file"
// in the file list, even though git doesn't treat them that way.
const filesMap: Map<string, {fileInfo: FileInfo}> = new Map()
files.forEach((file) => {
const fileParts = file.split("/")
const allPathsInFile = fileParts.reduce((accumulator, currentValue, index) => {
// Skip the first iteration, we have already added it as the initialValue
if (index === 0) { return accumulator }
accumulator.push(accumulator[accumulator.length - 1] + '/' + currentValue)
return accumulator
}, [fileParts[0]])
allPathsInFile.forEach((path) => {
filesMap.set(path, {
get fileInfo() {
return {
contents: "", // TODO: implement this part
lastModified: new Date(),
blameLines: []
}
}
})
})
})
return filesMap
}
export const addBranchToCommitsMap = async(branchName: string, repoLocation: string, commits: Repository['commits']): Promise<void> => {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
import childProcess from 'child_process'
const exec = util.promisify(childProcess.exec)
import { getGitDiffsFromPatchText} from '../../helpers.ts'
import { type Repository } from '../../dataTypes.ts'
export const getFileList = async (
branchName: string, repoLocation: string
) : Promise<Set<string>> => {
const command = `git -C ${repoLocation} ls-tree -r --name-only ${branchName}`
const result = await exec(command)
let files = result.stdout.split("\n").filter(item => item.length > 0 && item != ".")
const fileSet: Set<string> = new Set(files)
return fileSet
}
export const addBranchToCommitsMap = async(branchName: string, repoLocation: string, commits: Repository['commits']): Promise<void> => {141 142 143 144 145 146
// js Date() exects milliseconds
diffs,
parent,
cachedFiles: new Map(),
})
} while (gitLogSubset.length > 1)
}141 142 143 144 145
// js Date() exects milliseconds
diffs,
parent,
})
} while (gitLogSubset.length > 1)
}