Turn some NJK templates into typescript files

88b515f196bbaeb8cc0cdcdc1768f0ba15622a50

Tucker McKnight | Sun Dec 21 2025

Turn some NJK templates into typescript files

Added an htmlPage wrapper for the common header/nav elements.

Repo, files, and file pages done so far.
frontend/main.js:1
Before
1
import {branchesListItems} from '../dist/js_templates/repo.js'
After
1
import {branchesListItems} from '../dist/js_templates/common/htmlPage.js'
frontend/top.js:16
Before
16
  window['currentTheme'] = localStorage.getItem("theme") || "auto"
After
16
  // window['currentTheme'] = localStorage.getItem("theme") || "auto"
  window['currentTheme'] = "light"
js_templates/common/htmlPage.ts:55
Before
55
                      <a class="nav-link" href="#">${repo.name}</a>
After
55
                      <a class="nav-link" href="${nav.repoCurrentBranchHome()}">${repo.name}</a>
js_templates/common/htmlPage.ts:104
Before
104
105
                      <a class="nav-link active" aria-current="page" href="${nav.repoCurrentBranchHome()}">Home</a>
                      <a class="nav-link" href="${nav.repoCurrentBranchFiles()}">Files</a>
After
104
105
                      <a class="nav-link ${data.navTab === 'home' ? 'active&#39; : ''}&quot; aria-current="page" href="${nav.repoCurrentBranchHome()}">Home</a>
                      <a class="nav-link ${data.navTab === &#39;files' ? 'active' : ''}&quot; href="${nav.repoCurrentBranchFiles()}">Files</a>
js_templates/file.ts:0
Before
0
After
0
export default async (eleventyConfig: any, data: any) => {
  const getFileName = eleventyConfig.getFilter("getFileName")
  const isDirectory = eleventyConfig.getFilter("isDirectory")
  const topLevelFilesOnly = eleventyConfig.getFilter("topLevelFilesOnly")
  const getDirectoryContents = eleventyConfig.getFilter("getDirectoryContents")
  const getFileContents = eleventyConfig.getFilter("getFileContents")
  const getFileLastTouchInfo = eleventyConfig.getFilter("getFileLastTouchInfo")
  const getRelativePath = eleventyConfig.getFilter("getRelativePath")
  const slugify = eleventyConfig.getFilter("slugify")
  const lineNumbers = eleventyConfig.getFilter("lineNumbers")
  const highlightCode = eleventyConfig.getFilter("highlightCode")
  const languageExtension = eleventyConfig.getFilter("languageExtension")

  return `
    <h3>./${data.fileInfo.file}</h3>
    <p>Files snapshot from <span class="font-monospace">${data.fileInfo.branchName}</span></p>
    ${isDirectory(data.fileInfo.file, data.fileInfo.repoName, data.fileInfo.branchName) ?
    `<ul class="list-group">
    ${topLevelFilesOnly(getDirectoryContents(data.fileInfo.repoName, data.fileInfo.branchName, data.fileInfo.file), data.fileInfo.file + '/').map((dir) => {
      return `<li class="list-group-item">
        ${dir.isDirectory ?
        `<i class="bi bi-folder-fill"></i>`
        : `<i class="bi bi-file-earmark"></i>`}
        <a href="${data.reposPath}/${slugify(data.fileInfo.repoName)}/branches/${slugify(data.fileInfo.branchName)}/files/${slugify(dir.fullPath)}.html">${getRelativePath(data.fileInfo.file, dir.name)}</a>
      </li>`
    }).join('')}
    </ul>`
    :
    `<div class="row py-2">
      <div class="col-auto">
        <div class="form-check form-switch">
          <input class="form-check-input" type="checkbox" role="switch" id="showLastTouch">
          <label class="form-check-label" for="showLastTouch">Show last line change</label>
        </div>
      </div>
      <div class="col">
        <a href="${data.reposPath}/${slugify(data.fileInfo.repoName)}/branches/${slugify(data.fileInfo.branchName)}/raw/${slugify(data.fileInfo.file)}">View raw file</a>
      </div>
    </div>
    <div class="row">
      <div class="col-auto p-0">
        <code style="white-space: pre;"><pre class="language-text">${lineNumbers(await getFileContents(data.fileInfo.repoName, data.fileInfo.branchName, data.fileInfo.file)).map((lineNumber) => {
          return lineNumber
        }).join('\n')}</pre></code>
      </div>
      <div id="annotations" class="col-auto d-none p-0">
        <code style="white-space: pre;"><pre class="language-text">${
          (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)}/patches/${annotation.sha}">${annotation.sha.substr(0, 6)}</a> ${annotation.author}`
            }
          ).join('\n')
        }</pre></code>
      </div>
      <div class="col overflow-scroll p-0">
        <code>
          ${
            highlightCode(
              await getFileContents(
                data.fileInfo.repoName,
                data.fileInfo.branchName,
                data.fileInfo.file
              ),
              languageExtension(
                data.fileInfo.file,
                data.fileInfo.repoName
              )
            )
          }
        </code>
      </div>
    </div>
    `}
    
    <script type="text/javascript">
      const toggleLastTouch = (event) => {
        const isOn = event.target.checked
        const annotations = document.getElementById("annotations")
        if (isOn) {
          annotations.classList.remove("d-none")
        } else {
          annotations.classList.add("d-none")
        }
      }
    
      document.getElementById("showLastTouch")?.addEventListener('click', toggleLastTouch)
    </script>
  `
}
js_templates/files.ts:1
Before
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  const files = topLevelFilesOnly('', branch.fileList)
    <div class=&quot;row&quot;>
      <div class="col">
        <ul class="list-group">
        ${files.map(files) => {
            return `
              <li class="list-group-item">
                ${file.isDirectory ? '<i class=&quot;bi bi-folder-fill&quot;>;</i>' : '&lt;i class="bi bi-file-earmark"></i>&#39;
                <a href="${data.reposPath}/${slugify(data.branchInfo.repoName)}/branches/${slugify(branchInfo.branchName)}/files/${slugify(file.fullPath)}.html">${file.name}</a>
              </li>
            `
        }}
        &lt;/ul&gt;
      </div>
    </div>
After
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { type SortedFileList, type Repository } from "../src/dataTypes.ts"

  const files: SortedFileList = topLevelFilesOnly(branch.fileList, '')
    <h3&gt;./&lt;/h3>
    <p>Files snapshot from <span class="font-monospace">${data.branchInfo.branchName}</span></p>
    <ul class="list-group">
    ${files.map((file) => {
        return `
          <li class="list-group-item">
            ${file.isDirectory ? '<span&gt;&amp;#x1F4C1;</span>' : ''}
            <a href="${data.reposPath}/${slugify(data.branchInfo.repoName)}/branches/${slugify(data.branchInfo.branchName)}/files/${slugify(file.fullPath)}.html">${file.name}</a>
          </li>
        `
    }).join(&#39;&#39;)}
    </ul>
js_templates/repo.ts:1
Before
1
import { type ReposConfiguration } from '../src/configTypes.ts'
After
1
main.ts:9
Before
9
After
9
import { type SortedFileList } from './src/dataTypes.ts'
import filesJsTemplate from './js_templates/files.ts'
import fileJsTemplate from './js_templates/file.ts'
main.ts:113
Before
113
After
113
    try {
      code.split('')
    }
    catch (error) {
      console.log(code)
    }
main.ts:149
Before
149
  eleventyConfig.addFilter("topLevelFilesOnly", (files: Array<string>, currentLevel: string) => {
After
149
  eleventyConfig.addFilter("topLevelFilesOnly", (files: Array<string>, currentLevel: string): SortedFileList => {
main.ts:286
Before
286
287
288
289
  // FILE.NJK
  const fileTemplate = fsImport.readFileSync(`${import.meta.dirname}/templates/file.njk`).toString()
    'repos/file.njk',
    topLayoutPartial + fileTemplate + bottomLayoutPartial,
After
286
287
288
289
  // FILE.TS
    'repos/file.11ty.js',
    htmlPage(reposConfiguration, eleventyConfig, fileJsTemplate),
main.ts:344
Before
344
345
346
347
  // FILES.NJK
  const filesTemplate = fsImport.readFileSync(`${import.meta.dirname}/templates/files.njk`).toString()
    'repos/files.njk',
    topLayoutPartial + filesTemplate + bottomLayoutPartial,
After
344
345
346
347
  // FILES.TS
    'repos/files.11ty.js',
    htmlPage(reposConfiguration, eleventyConfig, filesJsTemplate),
main.ts:379
Before
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
  // REPO.NJK
  const repoTemplate = fsImport.readFileSync(`${import.meta.dirname}/templates/repo.njk`).toString()
  eleventyConfig.addTemplate(
    'repos/repo.njk',
    topLayoutPartial + repoTemplate + bottomLayoutPartial,
    {
      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)}/`
      },
      eleventyComputed: {
        nav: {
          repoName: (data) => data.branch.repoName,
          branchName: (data) => data.branch.branchName,
          path: "",
        },
        currentRepo: (data) => reposData.find(repo => {
          return repo.name === data.branch.repoName
        }),
        currentBranch: (data) => reposData.find(repo => {
          return repo.name === data.branch.repoName
        }).branches.find(branch => {
          return branch.name === data.branch.branchName
        }),
      },
      navTab: "landing",
    }
  )

  const template = htmlPage(reposConfiguration, eleventyConfig, repoJsTemplate)

    template,
After
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
    htmlPage(reposConfiguration, eleventyConfig, repoJsTemplate),
main.ts:429
Before
429
        return `${reposPath}/${eleventyConfig.getFilter("slugify")(repoName)}/new/${eleventyConfig.getFilter("slugify")(branchName)}/`
After
429
        return `${reposPath}/${eleventyConfig.getFilter("slugify")(repoName)}/branches/${eleventyConfig.getFilter("slugify")(branchName)}/`
main.ts:446
Before
446
After
446
      navTab: "home"
scss/design-board.scss:11
Before
11
12
$blue: #7388FA;
$lightblue: #73B2FA;
After
11
12
$blue: #556DF0;
$lightblue: #7388FA;
scss/design-board.scss:30
Before
30
31
32
33
34
  background-color: $blue;
  border-top: 6px solid color.adjust($blue, $lightness: 15%);
  border-left: 6px solid color.adjust($blue, $lightness: 13%);
  border-bottom: 6px solid color.adjust($blue, $lightness: -10%);
  border-right: 6px solid color.adjust($blue, $lightness: -13%);
After
30
31
32
33
34
.list-group {
  --bs-list-group-border-width: 1px;
  --bs-list-group-border-color: #BAC1CA;
}
  background-color: $lightblue;
  border-top: 6px solid color.adjust($lightblue, $lightness: 15%);
  border-left: 6px solid color.adjust($lightblue, $lightness: 13%);
  border-bottom: 6px solid color.adjust($lightblue, $lightness: -10%);
  border-right: 6px solid color.adjust($lightblue, $lightness: -13%);
src/dataTypes.ts:27
Before
27
After
27

export type SortedFileList = Array<{
  name: string,
  fullPath: string,
  isDirectory: boolean,
}>