summaryrefslogtreecommitdiff
path: root/cgithub/src/scraper.ts
diff options
context:
space:
mode:
authorJoe Mou <dev@mou.fo>2026-08-11 08:09:47 -0400
committerJoe Mou <dev@mou.fo>2026-08-11 08:13:58 -0400
commit0be1fdd2635fbc49d7451d3711b5772e1e9990e2 (patch)
treed317fae68cce476f57eb50eb33e2c6d21be8a765 /cgithub/src/scraper.ts
parent6240cf1c6b73b7ed34ff5816b53333382c229f5c (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 'cgithub/src/scraper.ts')
-rw-r--r--cgithub/src/scraper.ts39
1 files changed, 39 insertions, 0 deletions
diff --git a/cgithub/src/scraper.ts b/cgithub/src/scraper.ts
index 39ad2cd..223839f 100644
--- a/cgithub/src/scraper.ts
+++ b/cgithub/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;