DEVDéveloppement

Astro : mes liens Obsidian restaient cassés, la faute à deux caches

Des liens [[…]] vers des articles bien présents, et pourtant affichés cassés, même après redémarrage. Ce qui se passait dans le cache d'Astro 7, et les deux corrections qui ont réglé l'affaire.

· 4 min de lecture

Ce blog accepte les liens Obsidian : j’écris [[foundry-data-model]] dans une note, et le site en fait un lien vers l’article. Si la note visée n’existe pas, le texte reste affiché sans lien. Un soir, j’ajoute six articles d’un coup, une série qui se renvoie la balle de lien en lien. Résultat : dans le premier article, tous les liens vers les cinq autres sont cassés. Les articles existent pourtant, chacun s’ouvre très bien à sa propre adresse.

Le plus énervant : redémarrer npm run dev ne changeait rien. Voici ce qui se passait, parce que le piège n’est pas propre à mon site.

Comment les liens sont fabriqués

Depuis Astro 7, le moteur Markdown par défaut s’appelle Sätteri. Il sait lire [[…]] tout seul (option wikilinks), et accepte des extensions qui retouchent le document. La mienne, dans src/obsidian.mjs, cherche la note visée parmi les fichiers de src/content/articles :

  • si elle existe, le lien pointe vers /articles/<nom>/ ;
  • sinon, le texte reste affiché, souligné en pointillés, avec une infobulle « article introuvable ».

Le détail qui compte : cette recherche avait lieu au moment où Astro met l’article en forme.

Premier problème : le cache des articles

Astro ne remet pas en forme un article à chaque fois. Il garde le HTML de chaque fichier Markdown dans un cache (le data store) et ne recommence que si le contenu du fichier a changé.

Mes six articles ont été enregistrés quasiment au même instant, pendant que npm run dev tournait. Le premier a été mis en forme avant que les autres existent : ses liens sont sortis cassés. Puis les autres fichiers sont arrivés… et le premier n’avait aucune raison d’être refait, puisque son texte n’avait pas bougé. Ses liens cassés étaient figés dans le cache.

Correction : vérifier les liens à l’affichage

Plutôt que de décider une fois pour toutes si un lien est valide, l’extension écrit maintenant dans le HTML les ingrédients du lien (note visée, section, texte choisi), dans un attribut data-wl. Un middleware Astro, src/middleware.mjs, relit ces ingrédients à chaque page servie et refait le lien avec la liste des articles du moment :

import { defineMiddleware } from 'astro:middleware';
import { refreshWikilinks } from './obsidian.mjs';

export const onRequest = defineMiddleware(async (context, next) => {
  const response = await next();
  if (!context.url.pathname.startsWith('/articles/')) return response;
  if (!response.headers.get('content-type')?.includes('text/html')) return response;
  const html = await response.text();
  return new Response(refreshWikilinks(html, context.url.pathname), {
    status: response.status,
    headers: response.headers,
  });
});

En développement, il tourne à chaque affichage : un lien devient actif dès que la note visée est créée, et redevient cassé si on la supprime. À la construction du site, il tourne une fois par page, avec la liste complète : le site publié est toujours juste.

Deuxième problème : le mauvais cache

Correction faite, j’ai vidé le cache avec npx astro sync --force et relancé npm run dev. Toujours cassé. Même après avoir arrêté tous les processus Node dans le Gestionnaire des tâches.

Le contenu du cache montrait pourtant des liens corrects. Sauf que ce n’était pas le bon fichier : sur ma machine, avec Astro 7.3, il y en a deux.

Commande Cache utilisé
astro build, astro sync node_modules/.astro/data-store.json
astro dev .astro/data-store.json, à la racine du projet

astro sync --force avait proprement vidé le premier. Le serveur de développement lisait le second, resté tel quel depuis l’arrivée des articles, avec ses dix-huit liens cassés à l’ancienne. Le middleware ne pouvait rien pour eux : ils dataient d’avant la correction et ne portaient pas l’attribut data-wl.

Correction : vider le cache à chaque démarrage

Dans package.json :

"scripts": {
  "dev": "astro dev --force",
  "build": "astro build --force"
}

--force vide le cache du contenu avant de démarrer. Sur un blog de quelques dizaines d’articles, ça coûte une ou deux secondes, et une modification de l’extension ne peut plus rester coincée derrière un vieux cache. Sur un site de milliers de pages, je ne le ferais pas systématiquement.

Ce que j’en retiens

  • Un cache qui se base sur le contenu d’un fichier ignore tout ce qui se passe autour. Si le résultat dépend d’autre chose, soit on le calcule plus tard (ici, à l’affichage), soit on accepte de vider le cache.
  • « Redémarrer » ne vide pas un cache sur disque. Il survit à l’arrêt du serveur, c’est même son rôle.
  • Deux commandes, deux caches. Avant de conclure qu’une correction ne marche pas, vérifier qu’on regarde le bon.
  • Plusieurs processus Node.js pour un seul npm run dev, c’est normal : npm lui-même, puis ce que lance Astro. Ce n’était pas la piste.

Commentaires

Les commentaires de cet article sont les réponses à son annonce sur Mastodon. Pour réagir, répondez-y depuis votre compte, quelle que soit votre instance.

Répondre sur Mastodon

Compte sur une autre instance ? Collez l'adresse du pouet dans la recherche de votre instance, puis répondez. Les réponses ne sont chargées depuis ludosphere.fr que si vous cliquez sur « Afficher les réponses ».