diff options
| author | Joe Mou <dev@mou.fo> | 2026-08-11 08:09:47 -0400 |
|---|---|---|
| committer | Joe Mou <dev@mou.fo> | 2026-08-11 08:13:58 -0400 |
| commit | b43ce92c7b16bdefedb7c4c2aef41f9417f1ddf6 (patch) | |
| tree | 29ff6b47a16e62a58e17921f2c1200249c1845c0 /src/scraper.ts | |
| parent | ecd01c4617ec0a6af05cfc4388bdc82986f25961 (diff) | |
Add language percentages to repo pages
GitHub no longer server-renders the repository sidebar; its React UI
lazily fetches /:owner/:repo/_sidebar as JSON, which carries the
language breakdown (name, percentage, color). getGitHubLanguages() uses
that endpoint, and a new /api/:owner/:repo/languages fragment route
renders the bar and legend the same way refs and latest-commit already
work, so repo page loads still cost a single scrape.
The latest-commit placeholder loader in refs.js is generalized over
.placeholder[data-src] so both placeholders share it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'src/scraper.ts')
| -rw-r--r-- | src/scraper.ts | 39 |
1 files changed, 39 insertions, 0 deletions
diff --git a/src/scraper.ts b/src/scraper.ts index 39ad2cd..223839f 100644 --- a/src/scraper.ts +++ b/src/scraper.ts @@ -285,6 +285,17 @@ export interface GitHubLatestCommit extends GitHubCommon { commit: LatestCommit; } +export interface Language { + // "Other" for the aggregate bucket GitHub reports without a name. + name: string; + percentage: number; + color: string | null; +} + +export interface GitHubLanguages extends GitHubCommon { + languages: Language[]; +} + export interface Ref { name: string; isDefault?: boolean; @@ -722,6 +733,34 @@ export async function getGitHubLatestCommit( }; } +interface SidebarPayload { + languages?: { + languages: { + // Absent for the "Other" bucket. + name?: string; + percentage: number; + color?: string; + }[]; + }; +} + +// The repository sidebar (languages, contributors, ...) is not rendered into +// the repo page at all; GitHub's own UI lazily fetches it as JSON from here. +export async function getGitHubLanguages(owner: string, repo: string): Promise<GitHubLanguages> { + const payload = await fetchGitHubJson<SidebarPayload>(`${owner}/${repo}/_sidebar`); + + const languages: Language[] = []; + for (const entry of payload.languages?.languages ?? []) { + languages.push({ + name: entry.name ?? "Other", + percentage: entry.percentage, + color: entry.color ?? null, + }); + } + + return { repo: { owner, name: repo }, languages }; +} + interface BranchRefEntry { name: string; isDefault: boolean; |
