github.ts 1.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142
  1. /**
  2. * Build-time GitHub star count. Fetched once when the site is built (the GitHub
  3. * Actions runner has network); falls back to a constant locally / offline so a
  4. * build never hangs or fails on the network. The result is memoized for the
  5. * lifetime of the build process, so rendering it on every page is a single API
  6. * call, not one per page.
  7. */
  8. function format(n: number): string {
  9. if (n >= 1000) {
  10. const k = n / 1000;
  11. const rounded = k >= 10 ? Math.round(k) : Math.round(k * 10) / 10;
  12. return `${String(rounded).replace(/\.0$/, '')}k`;
  13. }
  14. return String(n);
  15. }
  16. async function fetchStars(fallback: string): Promise<string> {
  17. try {
  18. const controller = new AbortController();
  19. const timeout = setTimeout(() => controller.abort(), 3000);
  20. const res = await fetch('https://api.github.com/repos/colbymchenry/codegraph', {
  21. headers: {
  22. Accept: 'application/vnd.github+json',
  23. 'User-Agent': 'codegraph-site',
  24. },
  25. signal: controller.signal,
  26. });
  27. clearTimeout(timeout);
  28. if (!res.ok) return fallback;
  29. const data = (await res.json()) as { stargazers_count?: number };
  30. return typeof data.stargazers_count === 'number' ? format(data.stargazers_count) : fallback;
  31. } catch {
  32. return fallback;
  33. }
  34. }
  35. let cached: Promise<string> | null = null;
  36. export function getStarsLabel(fallback = '22k'): Promise<string> {
  37. cached ??= fetchStars(fallback);
  38. return cached;
  39. }