Skip to content

Commit 8381e49

Browse files
jdnichollscclaude
andauthored
feat: fetch GitHub stars dynamically at build time (#10)
* feat: fetch GitHub stars dynamically at build time - Add src/utils/github.ts with GitHub API utilities - Add src/data/projects.ts with centralized project definitions - Update Header to display dynamic org total stars - Update projects and index pages to fetch repo stats at build time - Stars and forks are now always up-to-date when site is built Co-Authored-By: Claude Opus 4.5 <[email protected]> * chore: Update project GitHub URL --------- Co-authored-by: Claude Opus 4.5 <[email protected]>
1 parent 27776b5 commit 8381e49

File tree

5 files changed

+317
-178
lines changed

5 files changed

+317
-178
lines changed

src/components/Header.astro

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
---
2+
import { getOrgTotalStars, formatStars } from '../utils/github';
3+
24
interface Props {
35
currentPage?: string;
46
}
57
68
const { currentPage = 'home' } = Astro.props;
79
10+
// Fetch total stars for the organization at build time
11+
const totalStars = await getOrgTotalStars('proyecto26');
12+
const starsDisplay = totalStars > 0 ? formatStars(totalStars) : '4.5k'; // Fallback
13+
814
const navLinks = [
915
{ href: '/projects', label: 'Projects', id: 'projects' },
1016
{ href: '/games', label: 'Games', id: 'games' },
@@ -58,7 +64,7 @@ const navLinks = [
5864
class="hidden sm:flex items-center gap-2 px-4 py-2 rounded-lg bg-bg-hover border border-border-default text-text-secondary hover:text-text-primary hover:border-accent-coral transition-all duration-200"
5965
>
6066
<i data-lucide="github" class="w-4 h-4"></i>
61-
<span class="text-sm font-medium">4.5k</span>
67+
<span class="text-sm font-medium">{starsDisplay}</span>
6268
</a>
6369

6470
<!-- Mobile Menu Button -->

src/data/projects.ts

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
import { getRepoStats } from '../utils/github';
2+
3+
export type IconColor = 'coral' | 'teal' | 'orange' | 'blue';
4+
5+
export interface Project {
6+
title: string;
7+
subtitle: string;
8+
description: string;
9+
iconColor: IconColor;
10+
tags: string[];
11+
stars?: number;
12+
forks?: number;
13+
href: string;
14+
githubUrl?: string;
15+
}
16+
17+
// Project definitions without stats (will be enriched at build time)
18+
const projectDefinitions: Record<string, Omit<Project, 'stars' | 'forks'>[]> = {
19+
featured: [
20+
{
21+
title: 'react-native-inappbrowser',
22+
subtitle: 'React Native',
23+
description: 'InAppBrowser for React Native with support for Chrome Custom Tabs, Safari Services, and SafariViewController. Provides authentication flow, deep linking, and web browser capabilities.',
24+
iconColor: 'coral',
25+
tags: ['React Native', 'iOS', 'Android', 'Authentication'],
26+
href: 'https://github.com/proyecto26/react-native-inappbrowser',
27+
githubUrl: 'https://github.com/proyecto26/react-native-inappbrowser',
28+
},
29+
{
30+
title: 'RestClient',
31+
subtitle: 'Unity',
32+
description: 'Promise-based REST and HTTP client for Unity game development. Makes API calls simple and clean with async/await support and JSON serialization.',
33+
iconColor: 'teal',
34+
tags: ['Unity', 'C#', 'REST API', 'Promises'],
35+
href: 'https://github.com/proyecto26/RestClient',
36+
githubUrl: 'https://github.com/proyecto26/RestClient',
37+
},
38+
],
39+
web: [
40+
{
41+
title: 'Animatable Component',
42+
subtitle: 'Web Component',
43+
description: 'Web component for creating reusable animations using the Web Animations API. Framework agnostic and easy to integrate.',
44+
iconColor: 'orange',
45+
tags: ['Stencil', 'Web Components', 'WAAPI'],
46+
href: 'https://proyecto26.github.io/animatable-component',
47+
githubUrl: 'https://github.com/proyecto26/animatable-component',
48+
},
49+
{
50+
title: 'IonPhaser',
51+
subtitle: 'Phaser + Ionic',
52+
description: 'Web component to integrate Phaser game framework with Ionic, Angular, React, Vue, and other frameworks.',
53+
iconColor: 'blue',
54+
tags: ['Phaser', 'Ionic', 'Stencil'],
55+
href: 'https://github.com/proyecto26/ion-phaser',
56+
githubUrl: 'https://github.com/proyecto26/ion-phaser',
57+
},
58+
{
59+
title: 'ProjectX',
60+
subtitle: 'Full-Stack Template',
61+
description: 'A real-world event-driven Full-Stack TypeScript template designed for production-ready distributed applications.',
62+
iconColor: 'coral',
63+
tags: ['React', 'NestJS', 'Temporal'],
64+
href: 'https://github.com/proyecto26/projectx',
65+
githubUrl: 'https://github.com/proyecto26/projectx',
66+
},
67+
],
68+
mobile: [
69+
{
70+
title: 'nativescript-inappbrowser',
71+
subtitle: 'NativeScript',
72+
description: 'InAppBrowser plugin for NativeScript with Chrome Custom Tabs and Safari Services support.',
73+
iconColor: 'teal',
74+
tags: ['NativeScript', 'iOS', 'Android'],
75+
href: 'https://github.com/proyecto26/nativescript-inappbrowser',
76+
githubUrl: 'https://github.com/proyecto26/nativescript-inappbrowser',
77+
},
78+
{
79+
title: 'PokeDex GO',
80+
subtitle: 'NativeScript',
81+
description: 'Pokemon collection app built with NativeScript showcasing mobile development best practices.',
82+
iconColor: 'orange',
83+
tags: ['NativeScript', 'Mobile', 'Game'],
84+
href: 'https://github.com/proyecto26/PokeDex-GO',
85+
githubUrl: 'https://github.com/proyecto26/PokeDex-GO',
86+
},
87+
],
88+
curated: [
89+
{
90+
title: 'Awesome Unity',
91+
subtitle: 'Curated List',
92+
description: 'A curated list of awesome Unity games, resources, tutorials, and open source projects for game developers.',
93+
iconColor: 'coral',
94+
tags: ['Unity', 'Resources', 'Curated'],
95+
href: 'https://github.com/proyecto26/awesome-unity',
96+
githubUrl: 'https://github.com/proyecto26/awesome-unity',
97+
},
98+
{
99+
title: 'Awesome JSGames',
100+
subtitle: 'Curated List',
101+
description: 'A curated list of awesome JavaScript games built with HTML5, Canvas, WebGL, and game engines.',
102+
iconColor: 'teal',
103+
tags: ['JavaScript', 'HTML5', 'Games'],
104+
href: 'https://github.com/proyecto26/awesome-jsgames',
105+
githubUrl: 'https://github.com/proyecto26/awesome-jsgames',
106+
},
107+
],
108+
};
109+
110+
/**
111+
* Enrich project with GitHub stats
112+
*/
113+
async function enrichProject(project: Omit<Project, 'stars' | 'forks'>): Promise<Project> {
114+
const githubUrl = project.githubUrl || project.href;
115+
116+
if (githubUrl?.includes('github.com')) {
117+
const stats = await getRepoStats(githubUrl);
118+
if (stats) {
119+
return { ...project, stars: stats.stars, forks: stats.forks };
120+
}
121+
}
122+
123+
return project;
124+
}
125+
126+
/**
127+
* Get all projects with GitHub stats (fetched at build time)
128+
*/
129+
export async function getProjects(): Promise<{
130+
featured: Project[];
131+
web: Project[];
132+
mobile: Project[];
133+
curated: Project[];
134+
}> {
135+
const [featured, web, mobile, curated] = await Promise.all([
136+
Promise.all(projectDefinitions.featured.map(enrichProject)),
137+
Promise.all(projectDefinitions.web.map(enrichProject)),
138+
Promise.all(projectDefinitions.mobile.map(enrichProject)),
139+
Promise.all(projectDefinitions.curated.map(enrichProject)),
140+
]);
141+
142+
return { featured, web, mobile, curated };
143+
}
144+
145+
/**
146+
* Get a subset of featured projects for the home page
147+
*/
148+
export async function getFeaturedProjects(limit = 6): Promise<Project[]> {
149+
const all = [
150+
...projectDefinitions.featured,
151+
...projectDefinitions.curated,
152+
...projectDefinitions.web.slice(0, 2),
153+
];
154+
155+
const enriched = await Promise.all(all.slice(0, limit).map(enrichProject));
156+
return enriched;
157+
}

src/pages/index.astro

Lines changed: 3 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -7,75 +7,10 @@ import ProjectCard from '../components/ProjectCard.astro';
77
import ServiceCard from '../components/ServiceCard.astro';
88
import GameCard from '../components/GameCard.astro';
99
import Button from '../components/Button.astro';
10+
import { getFeaturedProjects } from '../data/projects';
1011
11-
const projects = [
12-
{
13-
title: 'react-native-inappbrowser',
14-
subtitle: 'React Native',
15-
description: 'InAppBrowser for React Native with support for Chrome Custom Tabs and Safari Services.',
16-
iconColor: 'coral' as const,
17-
tags: ['React Native', 'iOS', 'Android'],
18-
stars: 1423,
19-
forks: 234,
20-
href: 'https://github.com/proyecto26/react-native-inappbrowser',
21-
githubUrl: 'https://github.com/proyecto26/react-native-inappbrowser',
22-
},
23-
{
24-
title: 'RestClient',
25-
subtitle: 'Unity',
26-
description: 'Promise-based REST and HTTP client for Unity game development.',
27-
iconColor: 'teal' as const,
28-
tags: ['Unity', 'C#'],
29-
stars: 1312,
30-
forks: 189,
31-
href: 'https://github.com/proyecto26/RestClient',
32-
githubUrl: 'https://github.com/proyecto26/RestClient',
33-
},
34-
{
35-
title: 'Animatable Component',
36-
subtitle: 'Web Component',
37-
description: 'Web component for creating reusable animations using the Web Animations API.',
38-
iconColor: 'orange' as const,
39-
tags: ['TypeScript', 'Web Components'],
40-
stars: 258,
41-
forks: 42,
42-
href: 'https://proyecto26.github.io/animatable-component',
43-
githubUrl: 'https://github.com/proyecto26/animatable-component',
44-
},
45-
{
46-
title: 'IonPhaser',
47-
subtitle: 'Phaser + Ionic',
48-
description: 'Web component to integrate Phaser game framework with Ionic and other frameworks.',
49-
iconColor: 'blue' as const,
50-
tags: ['Phaser', 'Ionic'],
51-
stars: 252,
52-
forks: 38,
53-
href: 'https://github.com/proyecto26/ion-phaser',
54-
githubUrl: 'https://github.com/proyecto26/ion-phaser',
55-
},
56-
{
57-
title: 'Awesome Unity',
58-
subtitle: 'Curated List',
59-
description: 'A curated list of awesome Unity games, resources, and tutorials for game developers.',
60-
iconColor: 'coral' as const,
61-
tags: ['Unity', 'Resources'],
62-
stars: 1300,
63-
forks: 86,
64-
href: 'https://github.com/proyecto26/awesome-unity',
65-
githubUrl: 'https://github.com/proyecto26/awesome-unity',
66-
},
67-
{
68-
title: 'Awesome JSGames',
69-
subtitle: 'Curated List',
70-
description: 'A curated list of awesome JavaScript Games built with HTML5 and game engines.',
71-
iconColor: 'teal' as const,
72-
tags: ['JavaScript', 'HTML5'],
73-
stars: 892,
74-
forks: 112,
75-
href: 'https://github.com/proyecto26/awesome-jsgames',
76-
githubUrl: 'https://github.com/proyecto26/awesome-jsgames',
77-
},
78-
];
12+
// Fetch featured projects with GitHub stats at build time
13+
const projects = await getFeaturedProjects(6);
7914
8015
const services = [
8116
{

src/pages/projects.astro

Lines changed: 8 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -4,116 +4,15 @@ import Header from '../components/Header.astro';
44
import Footer from '../components/Footer.astro';
55
import ProjectCard from '../components/ProjectCard.astro';
66
import Button from '../components/Button.astro';
7+
import { getProjects } from '../data/projects';
8+
import { getOrgTotalStars, formatStars } from '../utils/github';
79
8-
const featuredProjects = [
9-
{
10-
title: 'react-native-inappbrowser',
11-
subtitle: 'React Native',
12-
description: 'InAppBrowser for React Native with support for Chrome Custom Tabs, Safari Services, and SafariViewController. Provides authentication flow, deep linking, and web browser capabilities.',
13-
iconColor: 'coral' as const,
14-
tags: ['React Native', 'iOS', 'Android', 'Authentication'],
15-
stars: 1423,
16-
forks: 234,
17-
href: 'https://github.com/proyecto26/react-native-inappbrowser',
18-
githubUrl: 'https://github.com/proyecto26/react-native-inappbrowser',
19-
},
20-
{
21-
title: 'RestClient',
22-
subtitle: 'Unity',
23-
description: 'Promise-based REST and HTTP client for Unity game development. Makes API calls simple and clean with async/await support and JSON serialization.',
24-
iconColor: 'teal' as const,
25-
tags: ['Unity', 'C#', 'REST API', 'Promises'],
26-
stars: 1312,
27-
forks: 189,
28-
href: 'https://github.com/proyecto26/RestClient',
29-
githubUrl: 'https://github.com/proyecto26/RestClient',
30-
},
31-
];
10+
// Fetch all project data with GitHub stats at build time
11+
const { featured: featuredProjects, web: webProjects, mobile: mobileProjects, curated: curatedLists } = await getProjects();
3212
33-
const webProjects = [
34-
{
35-
title: 'Animatable Component',
36-
subtitle: 'Web Component',
37-
description: 'Web component for creating reusable animations using the Web Animations API. Framework agnostic and easy to integrate.',
38-
iconColor: 'orange' as const,
39-
tags: ['Stencil', 'Web Components', 'WAAPI'],
40-
stars: 258,
41-
forks: 42,
42-
href: 'https://proyecto26.github.io/animatable-component',
43-
githubUrl: 'https://github.com/proyecto26/animatable-component',
44-
},
45-
{
46-
title: 'IonPhaser',
47-
subtitle: 'Phaser + Ionic',
48-
description: 'Web component to integrate Phaser game framework with Ionic, Angular, React, Vue, and other frameworks.',
49-
iconColor: 'blue' as const,
50-
tags: ['Phaser', 'Ionic', 'Stencil'],
51-
stars: 252,
52-
forks: 38,
53-
href: 'https://github.com/proyecto26/ion-phaser',
54-
},
55-
{
56-
title: 'ProjectX',
57-
subtitle: 'Full-Stack Template',
58-
description: 'A real-world event-driven Full-Stack TypeScript template designed for production-ready distributed applications.',
59-
iconColor: 'coral' as const,
60-
tags: ['React', 'NestJS', 'Temporal'],
61-
stars: 45,
62-
forks: 8,
63-
href: 'https://github.com/proyecto26/MyAPI',
64-
githubUrl: 'https://github.com/proyecto26/MyAPI',
65-
},
66-
];
67-
68-
const mobileProjects = [
69-
{
70-
title: 'nativescript-inappbrowser',
71-
subtitle: 'NativeScript',
72-
description: 'InAppBrowser plugin for NativeScript with Chrome Custom Tabs and Safari Services support.',
73-
iconColor: 'teal' as const,
74-
tags: ['NativeScript', 'iOS', 'Android'],
75-
stars: 89,
76-
forks: 21,
77-
href: 'https://github.com/proyecto26/nativescript-inappbrowser',
78-
githubUrl: 'https://github.com/proyecto26/nativescript-inappbrowser',
79-
},
80-
{
81-
title: 'PokeDex GO',
82-
subtitle: 'NativeScript',
83-
description: 'Pokemon collection app built with NativeScript showcasing mobile development best practices.',
84-
iconColor: 'orange' as const,
85-
tags: ['NativeScript', 'Mobile', 'Game'],
86-
stars: 156,
87-
forks: 34,
88-
href: 'https://github.com/proyecto26/PokeDex-GO',
89-
githubUrl: 'https://github.com/proyecto26/PokeDex-GO',
90-
},
91-
];
92-
93-
const curatedLists = [
94-
{
95-
title: 'Awesome Unity',
96-
subtitle: 'Curated List',
97-
description: 'A curated list of awesome Unity games, resources, tutorials, and open source projects for game developers.',
98-
iconColor: 'coral' as const,
99-
tags: ['Unity', 'Resources', 'Curated'],
100-
stars: 1300,
101-
forks: 86,
102-
href: 'https://github.com/proyecto26/awesome-unity',
103-
githubUrl: 'https://github.com/proyecto26/awesome-unity',
104-
},
105-
{
106-
title: 'Awesome JSGames',
107-
subtitle: 'Curated List',
108-
description: 'A curated list of awesome JavaScript games built with HTML5, Canvas, WebGL, and game engines.',
109-
iconColor: 'teal' as const,
110-
tags: ['JavaScript', 'HTML5', 'Games'],
111-
stars: 892,
112-
forks: 112,
113-
href: 'https://github.com/proyecto26/awesome-jsgames',
114-
githubUrl: 'https://github.com/proyecto26/awesome-jsgames',
115-
},
116-
];
13+
// Get total org stars for the stats section
14+
const totalStars = await getOrgTotalStars('proyecto26');
15+
const starsDisplay = totalStars > 0 ? totalStars.toLocaleString() + '+' : '4,500+';
11716
---
11817

11918
<BaseLayout title="Projects - Proyecto 26" description="Explore our open source projects for web, mobile, and game development">
@@ -135,7 +34,7 @@ const curatedLists = [
13534
<!-- Stats -->
13635
<div class="flex flex-wrap justify-center gap-8 mb-16" data-animate="fade-up" data-delay="200">
13736
<div class="text-center">
138-
<div class="text-4xl font-bold gradient-text">4,500+</div>
37+
<div class="text-4xl font-bold gradient-text">{starsDisplay}</div>
13938
<div class="text-text-muted text-sm uppercase tracking-wide">GitHub Stars</div>
14039
</div>
14140
<div class="text-center">

0 commit comments

Comments
 (0)