Skip things that can't be done if plugins are missing

3d2bce97c01a5a43175463c0b37e5d74c18813a2

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.
main.ts:15
Before
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)
After
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)
main.ts:109
Before
108
109
110


111




112
113









  })

  eleventyConfig.addFilter("highlightCode", (code, language) => {
⁣
⁣
    return eleventyConfig.javascript.functions.highlight(language, code)
⁣
⁣
⁣
⁣
  })

⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
  eleventyConfig.addFilter("languageExtension", (filename, repoName) => {
After
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) => {
main.ts:399
Before
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,
⁣
⁣
    }
⁣
⁣
  )
}
After
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)
}
templates/repo.njk:1
Before
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">
After
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">
templates/repo.njk:9
Before
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">
After
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">