diff --git a/src/components/SiteHeader.astro b/src/components/SiteHeader.astro index 99120f2..49734f5 100644 --- a/src/components/SiteHeader.astro +++ b/src/components/SiteHeader.astro @@ -1,6 +1,7 @@ --- import { getLang, t, localePath, type Lang } from "~/i18n"; import { getStarCount, formatStars } from "~/lib/github"; +import { productRepos } from "~/lib/releases.mjs"; import { findDoc, getProduct } from "~/content/docs/_meta"; import SearchModal from "./SearchModal.astro"; import TwMark from "./TwMark.astro"; @@ -20,8 +21,6 @@ const sEn = t("en"); const sZh = t("zh-CN"); /** data-localize for a link on a bilingual page: the 404 page's script prefixes /zh-CN for Chinese visitors */ const localize = (path: string) => (bilingual ? path : undefined); -const stars = await getStarCount(); -const starLabel = formatStars(stars); // On the home page anchor links work as in-page jumps; on /docs/* and // /changelog they need to navigate back to the home first. @@ -46,6 +45,15 @@ const currentDocMeta = currentDocSlug ? findDoc(currentDocSlug, (docMatch?.[1] as "lite" | "core" | undefined) ?? "thinkwatch") : undefined; +// The GitHub pill shows ThinkWatch Lite's repository, except on the enterprise +// edition's page and its guides (/docs/), which show the enterprise one. +const enterprise = + /^\/thinkwatch(\/|$)/.test(cleanPath) || + (docMatch !== null && !docMatch[1] && docMatch[2] !== "lite" && docMatch[2] !== "core"); +const starRepo = enterprise ? productRepos.enterprise : productRepos.lite; +const stars = await getStarCount(starRepo); +const starLabel = formatStars(stars); + function targetPath(target: Lang): string { // The 404 page stands in for every missing path, and /404 itself does not // exist: its language choice leads to the home page. @@ -137,9 +145,10 @@ const zhAttrs = (attrs: Record) => (bilingual ? JSON.stringify(a @@ -311,8 +320,9 @@ const zhAttrs = (attrs: Record) => (bilingual ? JSON.stringify(a // Fetch live GitHub star count and update the pill { const el = document.querySelector("[data-star-count]"); - if (el) { - fetch("https://api.github.com/repos/ThinkWatchProject/ThinkWatch", { + const repo = el?.closest("[data-star-repo]")?.dataset.starRepo; + if (el && repo) { + fetch(`https://api.github.com/repos/${repo}`, { headers: { Accept: "application/vnd.github+json" }, }) .then((r) => r.ok ? r.json() : null) diff --git a/src/lib/github.ts b/src/lib/github.ts index 268693b..8adcc1c 100644 --- a/src/lib/github.ts +++ b/src/lib/github.ts @@ -9,23 +9,25 @@ export type { Product, Release }; const REPO = productRepos.enterprise; -let cached: number | null | undefined; +const starCache = new Map>(); -export async function getStarCount(): Promise { - if (cached !== undefined) return cached; - try { - const res = await fetch(`https://api.github.com/repos/${REPO}`, { headers: githubHeaders() }); - if (!res.ok) { - cached = null; - return null; - } - const data: { stargazers_count?: number } = await res.json(); - cached = typeof data.stargazers_count === "number" ? data.stargazers_count : null; - return cached; - } catch { - cached = null; - return null; +/** A repository's star count, fetched once per build and repository. */ +export function getStarCount(repo: string): Promise { + let cached = starCache.get(repo); + if (!cached) { + cached = (async () => { + try { + const res = await fetch(`https://api.github.com/repos/${repo}`, { headers: githubHeaders() }); + if (!res.ok) return null; + const data: { stargazers_count?: number } = await res.json(); + return typeof data.stargazers_count === "number" ? data.stargazers_count : null; + } catch { + return null; + } + })(); + starCache.set(repo, cached); } + return cached; } export function formatStars(n: number | null): string {