Présentez vos projets GitHub sur votre blog VitePress

- Read in english
Ressources: garabit

Dans le tutoriel d'aujourd'hui, nous allons approfondir notre exploration des chargeurs de données VitePress, entamée dans l'épisode sur la construction de la page du blog. Cette fois, nous mettrons l'accent sur la mise en valeur de vos projets GitHub en utilisant l'API GitHub. Passionnant, n'est-ce pas ?

Création d'une Nouvelle Page

Comme toujours, une nouvelle page commence par un fichier Markdown frais. Créons un fichier intitulé projects.md dans le répertoire src. Ce fichier, semblable à la page index du blog, ne contiendra que la frontmatter puisque le contenu sera chargé de manière dynamique.

md
---
layout: projects
title: Projects
description: Explore my projects.
---

Ensuite, nous devons créer le composant Vue qui formatera notre page. Insérons un nouveau fichier nommé ProjectsIndex.vue dans le répertoire .vitepress/theme/pages/Projects. Ce composant ressemblera au composant BlogIndex.vue :

vue
<template>
  projects
</template>

Enfin, lions la nouvelle page créée à la mise en page projects spécifiée dans le fichier projects.md. Pour y parvenir, ouvrons le fichier Layout.vue et intégrons cette page :

vue
<template>
  <div class="min-h-screen bg-[#FC88FF] py-16">
    <main class="mx-auto max-w-screen-md">
      <BlogIndex v-if="frontmatter.layout === 'blog'" />
      <BlogShow v-else-if="frontmatter.layout === 'blog-show'" />

      <ProjectsIndex v-else-if="frontmatter.layout === 'projects'" />
    </main>
  </div>
</template>

Naviguons vers http://localhost:3000/projects pour visualiser la nouvelle page, qui paraît plutôt vide pour le moment.

Notre nouvelle page de projets vide
Notre nouvelle page de projets vide

Récupération des Projets GitHub

Maintenant, nous devons récupérer des données de GitHub à mettre en valeur sur cette page. Nous allons utiliser tant l'API GitHub pour accéder aux données que les chargeurs de données VitePress pour extraire les données uniquement au moment du build time. Avoir des données en temps réel n'est pas nécessaire dans ce cas.

Tout d'abord, établissons un chargeur dans .vitepress/data/github-projects.data.ts :

ts
export default {
  async load() {
    return 'hello projects'
  }
}

N'oubliez pas que l'extension .data.ts est essentielle pour informer VitePress que ce fichier fonctionne comme un chargeur de données. VitePress utilise un plugin Vite pour détecter les imports depuis un fichier .data.ts. Dès que la détection est effectuée, le chargement des données a lieu, ne renvoyant que les résultats au client. Par conséquent, les données sont transformées au moment de la construction, offrant une disponibilité immédiate du navigateur, similaire aux modules virtuels dans Vite, mais plus adapté aux besoins de VitePress.

Ensuite, chargeons ce fichier dans le composant ProjectsIndex.vue :

vue
<script lang="ts" setup>
import { data } from '../../../data/github-projects.data'
</script>

<template>
  {{ data }}
</template>

Notre page affiche désormais le texte hello projects. C'est un bon début !

Avec cette configuration, nous pouvons maintenant récupérer les données des projets GitHub. Retournons dans notre fichier github-projects.data.ts et récupérons les données de l'API GitHub. Pour faciliter cela, nous allons installer le package octokit :

bash
pnpm add -D octokit

Ensuite, modifions le fichier github-projects.data.ts. Pour alléger les modifications futures, créons un tableau organisant tous nos projets en catégories.

Note

Ceci est un exemple, mais rappelez-vous que vous pouvez structurer les données comme vous le souhaitez.

ts
export default {
  async load() {
    const projects = [
      {
        title: 'Site Web',
        projects: ['barbapapazes/orion'],
      },
      {
        title: 'Modules Nuxt',
        projects: [
          'barbapapazes/nuxt-authorization',
          'barbapapazes/nuxt-payload-analyzer',
        ],
      },
      {
        title: 'Modèles Nuxt',
        projects: [
          'barbapapazes/gavarnie',
          'barbapapazes/slantire',
          'barbapapazes/the-green-chronicle',
        ],
      },
      {
        title: 'Outils',
        projects: ['barbapapazes/utils-ai'],
      },
    ]
  }
}

Ensuite, créons une instance Octokit pour récupérer les données :

ts
import { Octokit } from 'octokit'

export default {
  async load() {
    // ...

    const octokit = new Octokit()
  }
}

Récupérons les données en itérant sur chaque projet dans chaque catégorie :

ts
export default {
  async load() {
    // ...

    const loadedProjects = await Promise.all(
      projects.map(async (project) => {
        const projects = await Promise.all(
          project.projects.map(async (project) => {
            const [owner, repo] = project.split('/')

            const { data } = await octokit.rest.repos.get({
              owner,
              repo,
            })

            return {
              name: project,
              description: data.description,
              url: data.html_url,
            }
          }),
        )

        return { ...project, projects }
      }),
    )

    return loadedProjects
  }
}

Pour fournir de l'autocomplétion dans le composant Vue chargeant ce fichier, nous pouvons exporter un type :

ts
// ...

declare const data: {
  title: string
  projects: {
    name: string
    description: string
    url: string
  }[]
}[]
export { data }

// ...

Dépasser la Limite

Sans authentification, l'API GitHub a une limite de 60 requêtes par heure. Même si les données sont récupérées uniquement au démarrage du serveur, et non à chaque chargement de page, il est facile d'atteindre cette limite pendant le développement. Pour prévenir cela et augmenter la limite à 5 000 requêtes par heure, nous pouvons attribuer un jeton d'accès personnel à l'instance Octokit.

  1. Créons un fichier .env dans le répertoire source VitePress, qui est le répertoire racine de Vite, donc dans le répertoire src.

Warning

Ne publiez pas ce fichier. Ajoutez-le à votre fichier .gitignore.

  1. Insérons votre jeton d'accès personnel dans ce fichier :
VITE_GITHUB_TOKEN=your-personal-access-token

Créons un jeton d'accès personnel en suivant la documentation GitHub ou en utilisant la CLI GitHub gh auth token.

  1. Chargeons ce jeton dans le fichier github-projects.data.ts :
ts
import { join } from 'node:path'
import { Octokit } from 'octokit'
import { loadEnv } from 'vitepress'

export default {
  async load() {
    // ...

    const env = loadEnv('', join(process.cwd(), 'src'))
    const octokit = new Octokit({ auth: env.VITE_GITHUB_TOKEN })

    // ...
  }
}

Maintenant, nous pouvons récupérer les données de l'API GitHub sans vous soucier de la limite de requêtes.

Affichage des Projets

Ayant préparé la page et rassemblé les données, il est temps de les afficher.

Tout d'abord, créons un composant nommé ProjectCard.vue dans le répertoire .vitepress/theme/components. Il sera similaire au composant BlogCard.vue :

vue
<script lang="ts" setup>
import type { Project } from '../types/project'

defineProps<{
  project: Project
}>()
</script>

<template>
  <article
    class="group relative border-4 border-black bg-white p-6 shadow-[8px_8px_0_black] transition duration-150 ease-linear space-y-4 hover:shadow-[12px_12px_0_black] hover:-translate-x-1 hover:-translate-y-1"
  >
    <h2 class="text-2xl text-black font-medium">
      <a :href="project.url">
        {{ project.name }}
        <span class="absolute inset-0" />
      </a>
    </h2>

    <p v-if="project.description" class="text-lg text-black">
      {{ project.description }}
    </p>
  </article>
</template>

Comme nous l'observons, nous utilisons un type Project, que nous devons établir dans le fichier types/project.ts :

ts
export interface Project {
  name: string
  description: string | null
  url: string
}

Le grand avantage est que nous pouvons appliquer ce type dans le fichier github-projects.data.ts pour nous assurer que les données récupérées de l'API GitHub correspondent au format attendu pour l'affichage. C'est une stratégie efficace pour éviter les erreurs si nous modifions la structure des données à un endroit mais oublions de mettre à jour ailleurs.

Dans le fichier github-projects.data.ts, nous pouvons maintenant importer ce type et l'utiliser :

ts
// ...
import { Project } from '../theme/types/project'

declare const data: {
  title: string
  projects: Project[] 
}[]
export { data }

export default {
  async load() {
    // ...

    const loadedProjects = await Promise.all(
      projects.map(async (project) => {
        const projects = await Promise.all(
          project.projects.map(async (project) => {
            const [owner, repo] = project.split('/')

            const { data } = await octokit.rest.repos.get({
              owner,
              repo,
            })

            return {
              name: project,
              description: data.description,
              url: data.html_url,
            } satisfies Project
          }),
        )

        return { ...project, projects }
      }),
    )

    return loadedProjects
  }
}

Enfin, affichons les données dans le composant ProjectsIndex.vue :

vue
<script lang="ts" setup>
import { data } from '../../../data/github-projects.data'

const { frontmatter } = useData()
</script>

<template>
  <section class="space-y-16">
    <h1 class="text-6xl font-semibold">
      {{ frontmatter.title }}
    </h1>

    <div v-for="section in data" :key="section.title" class="space-y-20">
      <section class="space-y-10">
        <h2 class="text-4xl font-semibold">
          {{ section.title }}
        </h2>

        <ProjectCard
          v-for="project in section.projects"
          :key="project.url"
          :project="project"
        />
      </section>
    </div>

    <div class="flex justify-center">
      <ButtonPrimary href="/">
        Retour à l'accueil
      </ButtonPrimary>
    </div>
  </section>
</template>

Ce composant est simple. Il itère sur chaque catégorie de projets et les affiche à l'aide du composant ProjectCard. De plus, un bouton menant à la page d'accueil est inclus, conforme aux autres pages. Les données importées sont simples à consommer grâce à leur définition dans le fichier github-projects.data.ts, minimisant ainsi le besoin de manipulation au sein du composant. Cette méthodologie est préférable pour maintenir la simplicité dans le composant et placer la logique au moment de la construction.

Notre nouvelle page de projets avec les projets GitHub
Notre nouvelle page de projets avec les projets GitHub

Avec cet exemple pratique, nous avons maintenant une compréhension plus approfondie de la polyvalence et de l'efficacité des chargeurs de données VitePress. Ils permettent même l'intégration avec un CMS headless pour afficher votre contenu ou une base de données pour accéder à vos données. Les possibilités sont vraiment illimitées !