Tucker McKnight <tmcknight@instructure.com> | Sun Sep 28 2025
Skip things that can't be done if plugins are missing RSS feed creationg, syntax highlighting, and readme display rely on other 11ty plugins being installed in order for them to work. If those plugins are not installed, skip those parts of the site generation.
14 15 16 17 18 19 20 21
const ajv = new Ajv()
const exec = util.promisify(childProcess.exec)
// TODO: check if the render plugin is available and throw an error otherwise
// TODO: check if the highlight function is available
// TODO: throw an error if reposConfiguration is undefined
export default async function repoViewer(eleventyConfig, reposConfiguration: ReposConfiguration) {
const validator = ajv.compile(ConfigSchema)
const valid = validator(reposConfiguration)14 15 16 17 18
const ajv = new Ajv()
const exec = util.promisify(childProcess.exec)
export default async function repoViewer(eleventyConfig, reposConfiguration: ReposConfiguration) {
const validator = ajv.compile(ConfigSchema)
const valid = validator(reposConfiguration)108 109 110 111 112 113
})
eleventyConfig.addFilter("highlightCode", (code, language) => {
return eleventyConfig.javascript.functions.highlight(language, code)
})
eleventyConfig.addFilter("languageExtension", (filename, repoName) => {108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
})
eleventyConfig.addFilter("highlightCode", (code, language) => {
const highlighter = eleventyConfig?.javascript?.functions?.highlight
if (highlighter) {
return highlighter(language, code)
}
else {
return code
}
})
eleventyConfig.addAsyncFilter("renderContentIfAvailable", async (contentString, contentType) => {
const renderer = eleventyConfig?.javascript?.functions?.renderContent
if (renderer) {
return await renderer.bind({})(contentString, contentType)
}
else {
return `<pre>${contentString}</pre>`
}
})
eleventyConfig.addFilter("languageExtension", (filename, repoName) => {398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418
)
// FEED.NJK
const feedTemplate = fsImport.readFileSync(`${import.meta.dirname}/templates/feed.njk`).toString()
eleventyConfig.addTemplate(
`repos/feed.njk`,
feedTemplate,
{
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)}/patches.xml`
},
eleventyExcludeFromCollections: true,
}
)
}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 423 424 425 426 427
)
// FEED.NJK
// htmlBaseUrl is a function defined by the 11ty RSS plugin.
// Skip this virtual template if the 11ty RSS plugin is not being used.
let rssAvailable = false
if (eleventyConfig?.javascript?.functions?.htmlBaseUrl) {
rssAvailable = true
const feedTemplate = fsImport.readFileSync(`${import.meta.dirname}/templates/feed.njk`).toString()
eleventyConfig.addTemplate(
`repos/feed.njk`,
feedTemplate,
{
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)}/patches.xml`
},
eleventyExcludeFromCollections: true,
}
)
}
// This is used to show/hide the RSS feed link on the landing page.
eleventyConfig.addGlobalData('rssAvailable', rssAvailable)
}0 1 2 3 4
<div class="row">
<div class="col-md-8 col-sm-12 order-md-1 order-sm-2">
{{ branch.repoName | getReadMe(branch.branchName) | renderContent("md") | safe }}
</div>
<div class="col-md-4 col-sm-12 order-md-2 order-sm-1">
<div class="row">0 1 2 3 4
<div class="row">
<div class="col-md-8 col-sm-12 order-md-1 order-sm-2">
{{ branch.repoName | getReadMe(branch.branchName) | renderContentIfAvailable("md") | safe }}
</div>
<div class="col-md-4 col-sm-12 order-md-2 order-sm-1">
<div class="row">8 9 10 11 12 13 14 15
<div class="col-auto">
<h2 class="fs-6 my-0">Recent patches in {{branch.branchName}}</h2>
</div>
<div class="col-auto">
<a href="{{reposPath}}/{{ branch.repoName | slugify }}/branches/{{ branch.branchName | slugify }}/patches.xml" class="initialism">RSS<i class="bi bi-rss-fill ms-2" style="color: orange;"></i></a>
</div>
</div>
{% for patch in repos[branch.repoName].branches[branch.branchName].patches | batch(3) | first %}
<div class="card mt-2 mb-4">8 9 10 11 12 13 14 15 16 17
<div class="col-auto">
<h2 class="fs-6 my-0">Recent patches in {{branch.branchName}}</h2>
</div>
{% if rssAvailable %}
<div class="col-auto">
<a href="{{reposPath}}/{{ branch.repoName | slugify }}/branches/{{ branch.branchName | slugify }}/patches.xml" class="initialism">RSS<i class="bi bi-rss-fill ms-2" style="color: orange;"></i></a>
</div>
{% endif %}
</div>
{% for patch in repos[branch.repoName].branches[branch.branchName].patches | batch(3) | first %}
<div class="card mt-2 mb-4">