Tucker McKnight <tmcknight@instructure.com> | Sun Aug 09 2026
Make new repo page use the new languageGraph function
0 1 2 3 4 5 6 7 8
import m from 'mithril'
import { type Repository } from '../src/dataTypes.ts'
import { NavHelper } from './helpers/nav.ts'
import htmlPage from './common/htmlPage.ts'
import { colorAndPercentMap } from '../src/languageGraph.ts'
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")0 1 2 3 4 5 6 7 8 9 10 11 12 13
import m from 'mithril'
import { ReposConfigurationWithDefaultsApplied } from '../src/configTypes.ts'
import { type Repository } from '../src/dataTypes.ts'
import { NavHelper } from './helpers/nav.ts'
import htmlPage from './common/htmlPage.ts'
import { colorAndPercentMap } from '../src/languageGraph.ts'
export default async (
reposConfig: ReposConfigurationWithDefaultsApplied,
eleventyConfig: any,
data: any
) => {
const repo: Repository = data.currentRepo
const ref: Repository['branches'][0] | Repository['tags'][0] = data.currentRef
const renderContentIfAvailable = eleventyConfig.getFilter("renderContentIfAvailable")26 27 28 29 30
const readmeContent = await renderContentIfAvailable(await getReadMe(repo.name, ref.name), ref.name)
const pageContent = [
m('div', {class: "row"}, [
m('div', {class: "col"}, [26 27 28 29 30 31 32 33 34 35 36
const readmeContent = await renderContentIfAvailable(await getReadMe(repo.name, ref.name), ref.name)
const languageColors = []
const colorsAndPercentages = await colorAndPercentMap(reposConfig, ref, repo.files)
for (const language in colorsAndPercentages) {
languageColors.push({language, values: colorsAndPercentages[language]})
}
const pageContent = [
m('div', {class: "row"}, [
m('div', {class: "col"}, [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
])
]),
m('div', {class: "col-12 col-sm-6 d-flex"}, [
m('div', {class: "d-flex flex-column"}, [
topLanguagePercentages.map((percentTuple) => {
return m('div', {class: 'language-name me-1 d-flex justify-content-end align-items-end small text-white font-monospace flex-grow-1'}, percentTuple[0])
}),
otherLanguagePercent > 0
? m('div', {class: 'language-name me-1 d-flex justify-content-end align-items-end small text-white font-monospace flex-grow-1'}, 'other')
: null
]),
m('div', {class: "flex-grow-1 d-flex flex-column"}, [
topLanguagePercentages.map((percentTuple, index) => {
const color = colors[index % (colors.length - 1)].light
return m('div', {
class: 'language-col flex-grow-1',
style: `width: ${percentTuple[1] / largestPercent * 100}%; background-color: ${color};`
})
}),
otherLanguagePercent > 0
? m('div', {
class: 'language-col flex-grow-1',
style: `width: ${otherLanguagePercent / largestPercent * 100}%; background-color: ${colors[colors.length - 1].light};`
})
: null
])
]),
]),87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
])
]),
m('div', {class: "col-12 col-sm-6 d-flex"}, [
m('div', {class: "d-flex flex-column"}, languageColors.map((languageInfo) => {
return m('div', {
class: 'language-name me-1 d-flex justify-content-end align-items-end small text-white font-monospace flex-grow-1'
}, languageInfo.language)
})),
m('div', {class: "flex-grow-1 d-flex flex-column"}, [
languageColors.map((languageInfo) => {
const values = languageInfo.values
return m('div', {
class: 'language-col flex-grow-1',
style: `width: ${values.percentOfTotal * 100}%; background-color: ${values.color};`
})
}),
])
]),
]),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
}>
}
export type Repository = {
name: string,
description?: string,
cloneUrl: string,
defaultBranch: string,
branches: Array<{
name: string,
description?: string,
ahead: number,
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>,
}
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
}>
}
// A "ref" is a branch or tag. Branches have some fields that tags
// don't have, but both are a base type of Ref.
export type Ref = {
name: string,
sha: string,
fileList: Set<string>,
}
export type Repository = {
name: string,
description?: string,
cloneUrl: string,
defaultBranch: string,
branches: Array<Ref & {
description?: string,
ahead: number,
behind: number,
compareTo: string,
}>,
files: (filename: string, sha: string) => Promise<FileInfo>,
tags?: Array<Ref>,
commits?: Map<string, Commit>,
}
0 1 2
export const colorAndPercentMap = () => {
const languageCounts = new Map<string, number>()
const countPromises = Array.from(ref.fileList.keys()).map(async (currentFile) => {
return new Promise<void>(async (resolve) => {0 1 2 3 4 5 6 7 8 9
import { ReposConfigurationWithDefaultsApplied } from './configTypes.ts'
import { Ref, FileInfo } from './dataTypes.ts'
export const colorAndPercentMap = async (
reposConfig: ReposConfigurationWithDefaultsApplied,
ref: Ref,
files: (filename: string, sha: string) => Promise<FileInfo>,
) => {
const languageCounts = new Map<string, number>()
const countPromises = Array.from(ref.fileList.keys()).map(async (currentFile) => {
return new Promise<void>(async (resolve) => {9 10 11 12 13 14
resolve()
}
const fileLineCount = (await repo.files(currentFile, data.currentRef.sha)).contents.split('\n').length
languageCounts.set(fileExtension, (languageCounts.get(fileExtension) + fileLineCount) || fileLineCount)
resolve()
})9 10 11 12 13 14
resolve()
}
const fileLineCount = (await files(currentFile, ref.sha)).contents.split('\n').length
languageCounts.set(fileExtension, (languageCounts.get(fileExtension) + fileLineCount) || fileLineCount)
resolve()
})49 50 51 52
}
})
return colorAndPercentMap
}49 50 51 52 53 54 55 56 57 58 59
}
})
if (otherLanguagePercent > 0) {
colorAndPercentMap['other'] = {
color: colors[colors.length - 1].light,
percentOfTotal: otherLanguagePercent / largestPercent
}
}
return colorAndPercentMap
}