Aller au contenu principal

Séminaire

Objectifs

Rendu

Mise en place

  • Cloner le nouveau dépôt GitHub Classroom dans le répertoire du cours.
  • Ouvrir le dossier du dépôt dans Visual Studio Code.
  • Créer un fichier .gitignore à la racine du dépôt avec le contenu suivant :
./.gitignore
# macOS
.DS_Store

# Node.js
node_modules/

# Eleventy
_site/

Estimation

  • Créer un ficher report.md dans le dossier du dépôt.
  • Estimer le temps nécessaire pour réaliser ce travail dans le rapport.
    • Découper le travail en tâches pour faciliter l'estimation.
  • Une fois terminé, comparer le temps estimé avec le temps réellement passé.
TâcheTemps estiméTemps passéCommentaire
Estimation10m15m...
............
Total2h1h30...

Aventure Eleventy

Initialiser un projet npm avec la commande dans le terminal npm init. Un menu interactif (dont l'ordre peut différer) va s'afficher pour configurer le projet (Enter pour les valider) :

  • package name: choisir un nom pour l'aventure (en kebab-case). Par exemple : aventure-eleventy
  • version: 1.0.0 (par défaut)
  • description: laisser vide (par défaut)
  • entry point: index.js (par défaut)
  • test command: node --test
  • git repository: laisser vide (par défaut)
  • keywords: laisser vide (par défaut)
  • author: son nom
  • license: GPL-3.0
  • type: module

Vérifier le contenu du fichier package.json qui a été créé.

Installer Eleventy : npm install @11ty/eleventy.

Template de base

Créer un fichier index.md avec le contenu suivant :

index.md
---
layout: base.njk
title: Accueil
---

# {{ title }}

Bienvenue dans l'aventure !

Créer un dossier _includes avec un fichier base.njk avec le contenu suivant :

_includes/base.njk
<!doctype html>
<html lang="fr">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{{ title }}</title>
</head>
<body>
{{ content | safe }}
</body>
</html>

Voir le site en local : npx @11ty/eleventy --serve (le lien sera affiché dans la console)

  • Voir les fichiers générés dans le dossier _site.
  • Ctrl + C pour arrêter le serveur.
  • Ajouter les scripts suivants dans le fichier package.json :
package.json
{
...
"scripts": {
"start": "npx @11ty/eleventy --serve",
"build": "npx @11ty/eleventy",
"test": "node --test"
}
}
  • Tester les scripts npm run build et npm start. Que font-ils ?

Génération de pages

Créer un fichier _data/aventure.json avec le contenu suivant :

_data/aventure.json
[
{
"id": "rangement",
"name": "Rangement",
"description": "Vous décidez de faire du rangement dans votre maison. Par où commencer ?",
"options": [
{
"text": "Descendre à la cave.",
"next": "cave"
},
{
"text": "Monter au grenier.",
"next": "grenier"
}
]
},
{
"id": "cave",
"name": "Cave",
"description": "Vous trouvez une vieille boîte en carton.",
"options": [
{
"text": "Ouvrir la boîte.",
"next": "tresor"
},
{
"text": "Refermer la boîte.",
"next": "rangement"
}
]
}
]

Créer un fichier aventure-pages.html à la racine avec le contenu suivant :

aventure-pages.html
---
pagination:
data: aventure
size: 1
alias: aventure
permalink: "aventure/{{ aventure.id }}/"

layout: base.njk
eleventyComputed:
title: "{{ aventure.name }}"
---

<h1>{{ aventure.name }}</h1>
<p>{{ aventure.description }}</p>
<ul>
{% for option in aventure.options %}
<li><a href="../{{ option.next }}/">{{ option.text }}</a></li>
{% endfor %}
</ul>

Le fichier ci-dessus contient du Front Matter (entre les ---) qui permet de configurer la pagination d'Eleventy. Le langage dans cette section est du YAML, donc il faut respecter l'indentation et les espaces.

Ajouter un lien vers l'aventure dans le fichier index.md :

index.md
...

[Commencer](aventure/rangement/)

Observer le résultat.

  • On a séparé les données (_data/aventure.json) de la présentation (aventure-pages.html).
    • C'est une application du design pattern Modèle-Vue-Contrôleur (MVC), où le modèle est représenté par les données, la vue par le fichier HTML et le contrôleur est géré par Eleventy.
    • On peut maintenant facilement ajouter ou modifier les données sans toucher à la présentation.
  • Remarquer les éléments suivants :
    • Les données sont en JSON (JavaScript Object Notation) avec la structure suivante :
      • Une liste d'objets (représentant une étape de l'aventure) avec les propriétés suivantes :
        • id : identifiant unique (en kebab-case)
        • name : nom affiché
        • description : description de l'étape
        • options : liste d'options avec les propriétés suivantes :
          • text : texte affiché
          • next : identifiant de l'étape suivante
    • Les pages sont générées à partir des données grâce à la pagination d'Eleventy (dans aventure-pages.html).
      • C'est une page html qui sera crée pour chaque élément de la liste aventure.
        • data : nom du fichier de données dans le dossier _data (sans l'extension)
        • size : nombre d'éléments par page
        • alias : nom de la variable utilisée dans le template
        • permalink : URL de la page générée
        • layout : réutilisation du layout base.njk
        • eleventyComputed : variables calculées pour le template
          • title : titre "dynamique" de la page
    • Les données sont accessibles dans le template grâce à la variable aventure.
      • On peut alors afficher les propriétés de l'objet aventure (comme name, description et options) avec la syntaxe {{ variable }}.
      • Pour les listes, on utilise une boucle for pour afficher chaque élément : {% for item in list %}.
        • Ne pas oublier de fermer la boucle avec {% endfor %}.
      • Nunjucks est le moteur de template utilisé par Eleventy.
        • Il permet d'ajouter des variables, des boucles, des conditions, etc. dans les fichiers HTML.

Modifier _data/aventure.json pour ajouter les étapes suivantes :

  • grenier : Vous trouvez une vieille malle en bois. avec les liens :
    • Ouvrir la malle. vers tresor
    • Refermer la malle. vers tresor
  • trésor : Vous trouvez un trésor ! avec le lien :
    • Recommencer. vers rangement
Solution
_data/aventure.json
[
{
"id": "rangement",
"name": "Rangement",
"description": "Vous décidez de faire du rangement dans votre maison. Par où commencer ?",
"options": [
{
"text": "Descendre à la cave.",
"next": "cave"
},
{
"text": "Monter au grenier.",
"next": "grenier"
}
]
},
{
"id": "cave",
"name": "Cave",
"description": "Vous trouvez une vieille boîte en carton.",
"options": [
{
"text": "Ouvrir la boîte.",
"next": "tresor"
},
{
"text": "Refermer la boîte.",
"next": "rangement"
}
]
},
{
"id": "grenier",
"name": "Grenier",
"description": "Vous trouvez une vieille malle en bois.",
"options": [
{
"text": "Ouvrir la malle.",
"next": "tresor"
},
{
"text": "Refermer la malle.",
"next": "rangement"
}
]
},
{
"id": "tresor",
"name": "Trésor",
"description": "Vous trouvez un trésor !",
"options": [
{
"text": "Recommencer.",
"next": "rangement"
}
]
}
]

Voici un résultat avec son code.

Créer un commit et pousser les changements sur GitHub.

Déploiement

Pour déployer le site sur GitHub Pages, il faut d'abord générer les fichiers HTML, CSS et JS (dans _site). On automatise ce processus avec GitHub Actions.

Configurer GitHub Pages dans les paramètres du dépôt :

  • Sur la page GitHub du dépôt, aller dans Settings > Pages > Sous Build and deployment puis Source, sélectionner GitHub Actions.

Créer un fichier .github/workflows/deploy.yml (avec les dossiers nécessaires) avec le contenu suivant :

.github/workflows/deploy.yml
name: Deploy to GitHub Pages

on:
push:
branches:
- main

permissions:
pages: write
id-token: write
contents: read

jobs:
deploy:
name: Build & Deploy
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: lts/*
cache: npm
- name: Install dependencies
run: npm ci
- name: Build website
run: npm run build
- name: Upload Build Artifact
uses: actions/upload-pages-artifact@v4
with:
path: _site
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4

GitHub Actions va automatiquement générer le site et le déployer sur GitHub Pages à chaque push sur la branche main.

Ajouter le lien du site dans le rapport.

Liens

Noter que le site sur GitHub Pages est accessible à l'adresse https://hepl-bs1inf5.github.io/sem05-ssg-{pseudo}/, le projet n'est donc pas à la racine de l'URL comme en local.

Toujours utiliser des chemins relatifs pour les liens. Une solution alternative serait d'utiliser un plugin.

Personnalisation

Image

Ajouter une images pour illustrer une étape de l'aventure.

  • Créer un dossier images à la racine du dépôt et y ajouter une image (par exemple tresor.jpg).
  • Créer un fichier eleventy.config.js à la racine du dépôt avec le contenu suivant :
    eleventy.config.js
    export default function (eleventyConfig) {
    // Copie `images/` vers `_site/images/`
    eleventyConfig.addPassthroughCopy("images");
    }
  • Modifier le fichier _data/aventure.json pour ajouter une image à une étape de l'aventure, par exemple :
    _data/aventure.json
    [
    ...
    {
    "id": "tresor",
    "name": "Trésor",
    "description": "Vous trouvez un trésor !",
    "image": "tresor.jpg",
    "options": [
    {
    "text": "Recommencer",
    "next": "rangement"
    }
    ]
    }
    ]
  • Adapter le template aventure-pages.html pour afficher l'image si elle existe :
    aventure-pages.html
    ...
    {% if aventure.image %}
    <img src="../../images/{{ aventure.image }}" alt="{{ aventure.name }}" />
    {% endif %}
  • Pourquoi avoir utilisé les conditions ?
  • Pourquoi doit-on remonter de deux niveaux dans le chemin de l'image (../../images/{{ aventure.image }}) ?
  • Pourquoi utiliser des chemins relatifs pour les images ?
  • Vérifier que l'image s'affiche correctement en local et sur GitHub Pages.
  • Avancé : Ajouter plusieurs images pour différentes étapes de l'aventure.

Histoire personnalisée

Adapter l'histoire de l'aventure selon les séminaires précédents.

  • Pour ajouter du code CSS personnalisé, créer un fichier styles.css à la racine du dépôt.
  • Copier le fichier CSS dans le dossier _site :
    eleventy.config.js
    export default function (eleventyConfig) {
    ...
    eleventyConfig.addPassthroughCopy("styles.css");
    }
  • Ajouter un lien vers le fichier CSS dans le fichier _includes/base.njk.

Bootstrap

  • Ajouter Bootstrap en CDN dans le fichier _includes/base.njk.
    • Ajouter le <link> et le <script> dans la section <head>.
  • Adapter le style des pages.

Formateur

  • Vérifier que Prettier est bien configuré pour les fichiers JavaScript, JSON, Markdown et HTML.
    • Ouvrir un fichier JavaScript, JSON, Markdown ou HTML sur Visual Studio Code.
    • Ctrl + Shift + P
    • Taper "Format Document With..." et sélectionner "Configure Default Formatter...".
    • Sélectionner "Prettier - Code formatter".

Test

Exécuter la commande npm install -D eslint-plugin-prettier eslint-config-prettier.

Créer un fichier eslint.config.js à la racine du dépôt avec le contenu suivant :

eslint.config.js
import { defineConfig } from "eslint/config";
import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended";

export default defineConfig([eslintPluginPrettierRecommended]);

Créer un fichier test.js à la racine du dépôt avec le contenu suivant :

test.js
import test from "node:test";
import assert from "node:assert/strict";
import fs from "node:fs";
import { execSync } from "child_process";

test("présence des fichiers et dossiers", () => {
// Liste des chemins attendus dans le projet.
const expectedPaths = [
"_data/aventure.json",
"_includes/base.njk",
".git",
".github/workflows/deploy.yml",
".gitignore",
"aventure-pages.html",
"eleventy.config.js",
"eslint.config.js",
"index.md",
"package.json",
"report.md",
"test.js",
];
// Vérifie que chaque chemin existe dans le projet.
expectedPaths.forEach((path) => {
assert.ok(
fs.existsSync(path),
`Le fichier ou dossier ${path} doit exister.`,
);
});
});

test("validation des fichiers", () => {
// Exécute la validation en utilisant https://eslint.org/
try {
const output = execSync("npx eslint").toString();
console.log(output);
} catch (error) {
assert.fail(error.output);
}
});

test("construction du site", () => {
// Exécute la construction en utilisant https://www.11ty.dev/
try {
const output = execSync("npx @11ty/eleventy").toString();
console.log(output);
} catch (error) {
assert.fail(error.output);
}
});

Vérifier que les tests passent avec la commande node --test et corriger le code si cela n'est pas le cas.

Rapport

  • Expliquer brièvement les principales difficultés rencontrées et comment les résoudre.
  • Compléter les estimations.
  • Pousser tous les changements sur GitHub.

Bonus

  • Essayer Docusaurus qui est utilisé pour ce cours.

Références