website

#astro#js#html#css

git clone https://git.pyrossh.dev/website

木 Personal website of pyrossh. Built with astrojs, shiki, vite.


src/_data/repoFiles.js
3c79a68 1
import fs from "node:fs";
3c79a68 2
import path from "node:path";
ada6e53 3
import { readCache, writeCache } from "../_helpers/dataCache.js";
3c79a68 4
import {
3c79a68 5
  BINARY_EXTENSIONS,
3c79a68 6
  IMAGE_EXTENSIONS,
3c79a68 7
  LARGE_FILE_SIZE,
3c79a68 8
  discoverLocalRepos,
3c79a68 9
  getBlame,
3c79a68 10
  getFileHistory,
ada6e53 11
  getHeadSha,
ada6e53 12
  isDirty,
1c2fa8e 13
  toDataUri,
3c79a68 14
} from "../_helpers/gitRepos.js";
3c79a68 15
import { highlightCode } from "../_helpers/highlightCode.js";
ada6e53 16
import { mapPool } from "../_helpers/pool.js";
ada6e53 17
ada6e53 18
const CONCURRENCY = 8;
3c79a68 19
ada6e53 20
const renderFile = (repoDir, file) => {
ada6e53 21
  const full = path.join(repoDir, file.path);
ada6e53 22
  if (BINARY_EXTENSIONS.has(file.ext)) return { kind: "binary" };
ada6e53 23
  if (file.size > LARGE_FILE_SIZE) return { kind: "large" };
ada6e53 24
  if (IMAGE_EXTENSIONS.has(file.ext)) return { kind: "image", src: toDataUri(fs.readFileSync(full), file.ext) };
ada6e53 25
  const { html, lang } = highlightCode(fs.readFileSync(full, "utf8"), file.ext);
ada6e53 26
  return { kind: "text", html, lang };
ada6e53 27
};
ada6e53 28
3c79a68 29
export default async () => {
3c79a68 30
  const repos = discoverLocalRepos();
3c79a68 31
  const pages = [];
3c79a68 32
  for (const repo of repos) {
ada6e53 33
    const sha = getHeadSha(repo.dir);
ada6e53 34
    const cacheable = sha && !isDirty(repo.dir);
ada6e53 35
    const cacheKey = `files-${repo.id}`;
ada6e53 36
ada6e53 37
    let entries = cacheable ? readCache(cacheKey, sha) : null;
ada6e53 38
    if (!entries) {
ada6e53 39
      entries = await mapPool(repo.files, CONCURRENCY, async (file) => {
ada6e53 40
        const render = renderFile(repo.dir, file);
ada6e53 41
        const [history, blame] = await Promise.all([
ada6e53 42
          getFileHistory(repo.dir, file.path),
ada6e53 43
          render.kind === "text" ? getBlame(repo.dir, file.path) : [],
ada6e53 44
        ]);
ada6e53 45
        return { file, render, history, blame };
3c79a68 46
      });
ada6e53 47
      if (cacheable) writeCache(cacheKey, sha, entries);
3c79a68 48
    }
ada6e53 49
778e0a4 50
    // file.njk/file-history.njk/file-blame.njk only ever read `id`/`data`/
09c6a56 51
    // `tree`/`version` off `item.repo` — not `files`/`commits`/`readme`.
09c6a56 52
    // See the same note in repoCommits.js: Eleventy clones each page's data
09c6a56 53
    // slice, so keeping this slim avoids cloning the repo's full commit/
09c6a56 54
    // file lists on every single file page.
09c6a56 55
    const slimRepo = { id: repo.id, data: repo.data, tree: repo.tree, version: repo.version };
778e0a4 56
    for (const entry of entries) pages.push({ repoId: repo.id, repo: slimRepo, ...entry });
3c79a68 57
  }
3c79a68 58
  return pages;
3c79a68 59
};