website

#astro#js#html#css

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

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


src/_data/repoCommits.js
import { html as renderDiffHtml, parse as parseDiff } from "diff2html";
import { readCache, writeCache } from "../_helpers/dataCache.js";
import { discoverLocalRepos, getCommitDiff, getHeadSha } from "../_helpers/gitRepos.js";
import { mapPool } from "../_helpers/pool.js";

const CONCURRENCY = 8;

// Some repos have vendored/generated/binary-as-text blobs committed at some
// point in their history (a minified bundle, a compiled binary git doesn't
// detect as binary, ...) — `git show -p` dumps the whole file as a diff on
// every commit that touches it, and diff2html's per-line markup multiplies
// that further. Left unguarded, that's easily 100s of MB of HTML per repo,
// held for every commit at once across every repo — well past Node's default
// heap. Cap it the way GitHub's own diff viewer does: skip rendering past a
// size threshold rather than trying to syntax-highlight a multi-MB blob.
const MAX_DIFF_SIZE = 300 * 1024;
const DIFF_TOO_LARGE_HTML = '<p class="diff-too-large">This diff is too large to display.</p>';

const toDiffHtml = (diffText) => {
  const idx = diffText.indexOf("diff --git");
  const patch = idx >= 0 ? diffText.slice(idx) : "";
  if (!patch.trim()) return "";
  if (patch.length > MAX_DIFF_SIZE) return DIFF_TOO_LARGE_HTML;
  try {
    const json = parseDiff(patch);
    return renderDiffHtml(json, { drawFileList: true, matching: "lines" });
  } catch {
    return "";
  }
};

export default async () => {
  const repos = discoverLocalRepos();
  const pages = [];
  for (const repo of repos) {
    // Unlike repoFiles.js, none of this reads the working tree — `git
    // show` only ever sees committed history — so it's safe to cache on
    // HEAD sha alone, with no working-tree-clean requirement. That
    // matters because this repo (website) is usually dirty while it's
    // being actively worked on, and its commit history is the most
    // expensive part of the build.
    const sha = getHeadSha(repo.dir);
    const cacheKey = `commits-${repo.id}`;

    let entries = sha ? readCache(cacheKey, sha) : null;
    if (!entries) {
      entries = await mapPool(repo.commits, CONCURRENCY, async (commit) => {
        const diffText = await getCommitDiff(repo.dir, commit.hash);
        return { commit, diffHtml: toDiffHtml(diffText) };
      });
      if (sha) writeCache(cacheKey, sha, entries);
    }

    // Only `id`/`data`/`version` ever get read off `item.repo` in
    // commit.njk — not the full `repo` object's `files`/`tree`/`commits`/
    // `readme`. Eleventy's paginator deep-clones each page's data slice for
    // isolation, so embedding the whole repo (every commit's metadata,
    // every file, the tree, the README) here means that whole structure
    // gets cloned once per commit page — the actual multiplier behind the
    // OOM, not just the diff HTML size.
    const slimRepo = { id: repo.id, data: repo.data, version: repo.version };
    for (const entry of entries) pages.push({ repoId: repo.id, repo: slimRepo, ...entry });
  }
  return pages;
};