import { graphql } from "@octokit/graphql"; import { GitForkIcon, StarIcon } from "lucide-react"; import PageTitle from "../../components/PageTitle"; import Link from "../../components/Link"; import RelativeTime from "../../components/RelativeTime"; import commaNumber from "comma-number"; import config from "../../lib/config/constants"; import { metadata as defaultMetadata } from "../layout"; import type { Metadata } from "next"; import type { User, Repository } from "@octokit/graphql-schema"; import styles from "./page.module.css"; export const revalidate = 600; // 10 minutes export const metadata: Metadata = { title: "Projects", openGraph: { ...defaultMetadata.openGraph, title: "Projects", url: "/projects", }, alternates: { ...defaultMetadata.alternates, canonical: "/projects", }, }; type Project = { name: string; url: string; description?: string; language?: { name: string; color?: string; }; stars?: number; forks?: number; updatedAt: string; }; async function getRepos(): Promise { // don't fail the entire site build if the required API key for this page is missing if (!process.env.GITHUB_TOKEN) { console.warn(`ERROR: I can't fetch any GitHub projects without "GITHUB_TOKEN" set! Skipping for now...`); return null; } // https://docs.github.com/en/graphql/reference/objects#repository const { user } = await graphql<{ user: User }>( ` query ($username: String!, $sort: RepositoryOrderField!, $limit: Int) { user(login: $username) { repositories( first: $limit isLocked: false isFork: false ownerAffiliations: OWNER privacy: PUBLIC orderBy: { field: $sort, direction: DESC } ) { edges { node { name url description pushedAt stargazerCount forkCount primaryLanguage { name color } } } } } } `, { username: config.authorSocial.github, sort: "STARGAZERS", limit: 12, headers: { accept: "application/vnd.github.v3+json", authorization: `token ${process.env.GITHUB_TOKEN}`, }, } ); const results = user.repositories.edges as Array<{ node: Repository }>; const repos = results.map(({ node: repo }) => ({ name: repo.name, url: repo.url, description: repo.description as string, updatedAt: repo.pushedAt, stars: repo.stargazerCount, forks: repo.forkCount, language: repo.primaryLanguage as Project["language"], })); return repos; } export default async function Page() { const repos = await getRepos(); return ( <> Projects
{repos?.map((repo) => (
{repo.name} {repo.description &&

{repo.description}

}
{repo.language && (
{repo.language.color && ( )} {repo.language.name}
)} {repo.stars && repo.stars > 0 && (
{commaNumber(repo.stars)}
)} {repo.forks && repo.forks > 0 && (
{commaNumber(repo.forks)}
)} {/* only use relative "time ago" on client side, since it'll be outdated via SSG and cause hydration errors */}
))}

View more on{" "} {" "} GitHub...

); }