Skip to content

Solution Starlight - Symlinks avec docsLoader()

Problème initial :

  • Sync script complexe qui cassait la structure
  • Duplication des fichiers entre docs/, GettingStarted/ et apps/web/src/content/docs/
  • Starlight échouait à afficher les docs

Contrainte :

  • Starlight nécessite son propre docsLoader() pour fonctionner correctement
  • GettingStarted doit être disponible sur GitHub Actions pour le déploiement
  • Repo privé (pas public internet) mais GettingStarted ignoré pour éviter de “saturer” le repo

Documentation officielle :

  • Starlight docsLoader() charge les fichiers depuis src/content/docs/
  • Starlight sidebar autogenerate s’attend à ce que les chemins commencent par src/content/docs/
  • glob() avec un base externe produit des chemins qui ne commencent pas par ce préfixe
  • L’utilisation de glob() au lieu de docsLoader() cause l’erreur “Entry docs → docs was not found”

Résultat de recherche GitHub :

“Root cause: Starlight’s bundled docsLoader() — and its autogenerate sidebar logic — hardcode the docs location as src/content/docs/. Specifically, getRoutePathRelativeToCollectionRoot in utils/navigation.ts strips that prefix from entry.filePath before matching against the autogenerate directory argument. Our bare glob({ base: ‘../docs’ }) loader served content pages just fine but left every entry.filePath rooted at ../docs/…, so the prefix-strip never fired and the autogenerate filter returned zero matches.”

Section titled “SPEC : Solution implémentée - Symlinks + docsLoader()”

Décision :

  • Revenir à docsLoader() (requis par Starlight)
  • GettingStarted ne sera plus ignoré par git
  • Symlinks pour docs/ et GettingStarted/
  • Pas de script de sync nécessaire
  • NE PAS utiliser preserveSymlinks: true dans Vite (cause une erreur de module ‘piccolore’)

Avantages :

  • ✅ Pas de duplication de fichiers
  • ✅ Pas de script de sync complexe
  • ✅ Starlight fonctionne avec son docsLoader() natif
  • ✅ Édition directe dans les répertoires racine
  • ✅ Maintenable et simple
  • ✅ Disponible sur GitHub Actions (GettingStarted commité)

Inconvénients :

  • ❌ GettingStarted commité (peut “saturer” le repo)
  • ❌ Symlinks peuvent poser des problèmes sur Windows (mais GitHub Actions/Linux ok)

Retiré :

# Initial setup
/GettingStarted

GettingStarted est maintenant commité sur git.

Retour à docsLoader() :

import { defineCollection } from 'astro:content';
import { glob } from 'astro/loaders';
import { docsLoader, i18nLoader } from '@astrojs/starlight/loaders';
import { docsSchema, i18nSchema } from '@astrojs/starlight/schema';
import { z } from 'astro/zod';
const blog = defineCollection({
loader: glob({ base: './src/content/blog', pattern: '**/*.{md,mdx}' }),
schema: ({ image }) =>
z.object({
title: z.string(),
description: z.string(),
pubDate: z.coerce.date(),
updatedDate: z.coerce.date().optional(),
heroImage: z.optional(image()),
}),
});
const docs = defineCollection({
loader: docsLoader(),
schema: docsSchema(),
});
export const collections = {
blog,
docs,
i18n: defineCollection({ loader: i18nLoader(), schema: i18nSchema() }),
};

Note : Le champ visibility custom a été retiré car non nécessaire pour le moment.

Terminal window
cd apps/web/src/content/docs
ln -s ../../../docs docs
ln -s ../../../GettingStarted getting-started

Note importante sur les chemins :

  • Les symlinks doivent utiliser ../../../ (3 niveaux) car apps/web/src/content/docs/ est à 3 niveaux de profondeur depuis la racine
  • Structure résultante :
apps/web/src/content/docs/
├── 404.md
├── docs -> ../../../docs
└── getting-started -> ../../../GettingStarted

Sidebar avec directory :

sidebar: [
{
label: 'Getting Started',
items: [{ autogenerate: { directory: 'getting-started' } }],
},
{
label: 'Documentation Technique',
items: [{ autogenerate: { directory: 'docs' } }],
},
],

NE PAS utiliser preserveSymlinks :

vite: {
plugins: [
tailwindcss(),
],
// PAS de preserveSymlinks: true - cause une erreur "Cannot find module 'piccolore'"
optimizeDeps: {
exclude: ['@sanity/client', '@sanity/astro'],
},
server: {
fs: {
allow: ['../../node_modules', '.'],
},
watch: {
ignored: ['**/sanity.types.ts', '**/schema.json'],
},
},
ssr: {
noExternal: ['@sanity/preview-url-secret', '@sanity/client', '@sanity/astro', '@sanity/visual-editing', '@sanity/visual-editing/react'],
},
},

Sync-docs.sh retiré :

  • L’étape de sync a été supprimée de .github/workflows/deploy-dev.yml
  • Les symlinks sont gérés par git et fonctionnent sur GitHub Actions
  • ✅ Typecheck réussi (0 erreurs, 0 warnings)
  • ✅ Build réussi
  • ⚠️ Pagefind indexation (1 fichier HTML) - à vérifier dans le dev server
  • ✅ Sitemap généré
  1. Édition : Éditer directement dans docs/ et GettingStarted/ à la racine
  2. Accès : Starlight lit les fichiers via symlinks et docsLoader()
  3. Commit : Committer GettingStarted quand il y a des changements significatifs
  • Symlinks sur Windows : Peuvent poser des problèmes si vous développez sur Windows. Git sur Windows suit les symlinks par défaut mais ils nécessitent des permissions d’administrateur.
  • GitHub Actions : Les symlinks fonctionnent correctement sur Linux (GitHub Actions)
  • preserveSymlinks : NE PAS utiliser preserveSymlinks: true dans la config Vite - cela cause une erreur “Cannot find module ‘piccolore’” lors du typecheck/build
  • docsLoader() : Starlight exige docsLoader() pour sa collection docs - glob() ne fonctionne pas avec Starlight sidebar autogenerate
  1. Tester le dev server : Lancer task dev et vérifier l’accès aux docs
  2. Vérifier Pagefind : Confirmer que les docs sont bien indexés pour la recherche
  3. Test sur Windows : Si vous utilisez Windows, vérifier que les symlinks fonctionnent correctement

Cette solution utilise docsLoader() (requis par Starlight) combiné avec des symlinks pour permettre l’édition dans les répertoires racine. Les docs sont maintenant gérés comme une source unique de vérité dans leurs répertoires originaux, avec GettingStarted commité pour le déploiement.