Plan 07-02 shipped: useSeoMeta D-15 + useSchemaOrg Article/Breadcrumb on /blog/[slug], resolveOgImage helper + og-blog-default.jpg fallback. Curl SSR validated, typecheck green. Requirements satisfied: SEO-10, SEO-11, SEO-13, SEO-15.
7.2 KiB
phase, plan, subsystem, tags, status, completed, requirements, dependency_graph, tech_stack, key_files, decisions, metrics
| phase | plan | subsystem | tags | status | completed | requirements | dependency_graph | tech_stack | key_files | decisions | metrics | |||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 07-seo-blog | 04 | seo-sitemap |
|
shipped | 2026-04-22 |
|
|
|
|
|
|
Phase 7 Plan 4 : Sitemap Dynamique Blog Bilingue — Summary
One-liner : Endpoint Nitro server/api/__sitemap__/urls.ts qui alimente @nuxtjs/sitemap en URLs /fr/blog/{slug} + /en/blog/{slug} (non-draft) avec alternates hreflang cross-locale pour les paires bilingues.
Ce qui a été fait
Task 1 — feat(07-04) (commit 466bed0)
Création de server/api/__sitemap__/urls.ts :
defineSitemapEventHandler(async (event) => ...)— auto-import@nuxtjs/sitemapv8Promise.all([queryCollection(event, 'blog_fr')..., queryCollection(event, 'blog_en')...])— strings littérales (Pitfall 2), event first-arg (Pitfall 1).where('draft', '=', false).order('date', 'DESC').select('path', 'date', 'updated').all()— projection minimaleMap<slug, {fr?, en?}>alimentée viaextractSlug(path)pour détecter les paires- Pour chaque slug :
- si bilingue (
fr && en) →alternatives: [{hreflang:'fr'}, {hreflang:'en'}, {hreflang:'x-default' → FR}] - sinon →
alternatives: []
- si bilingue (
- Pousse 1 à 2 entrées
SitemapUrlpar slug aveclastmod = updated ?? date
Deviations from Plan
Deviation mineure — Rule 3 (blocking issue) : import explicite queryCollection depuis '@nuxt/content/server'
- Plan prescrivait : compter sur l'auto-import Nitro de
queryCollection - Problème :
pnpm typecheck(vue-tsc) ne résout pas l'auto-import Nitro pour ce fichier (signature client(collection)prise au lieu de la signature Nitro(event, collection)), erreursTS2554: Expected 1 arguments, but got 2. - Fix : ajout
import { queryCollection } from '@nuxt/content/server'— exporte la bonne signature Nitro(event, collection) => CollectionQueryBuilder. Runtime identique, types résolus. - Impact : aucun — le runtime Nitro route le même fichier
runtime/server.js. La fonction retourne correctement les données côté SSR dev.
Deviation mineure — Rule 1 (pitfall found during verify) : import initial defineSitemapEventHandler from '#imports' erroné
- Le plan importait explicitement
defineSitemapEventHandlerdepuis#imports→TS2305: has no exported member. defineSitemapEventHandlerest un auto-import global (déclaré par@nuxtjs/sitemapmodule setup), pas un export nommé de#imports.- Fix : suppression de l'import explicite — l'auto-import se résout correctement.
Aucune autre déviation. Aucun fichier hors server/api/__sitemap__/urls.ts modifié.
Acceptance Criteria — tous passés
Validés sur pnpm dev (port 3001, cf. 07-01) avec fixtures temporaires _sitemap-smoke.md (FR+EN, draft:false, updated:2026-04-22) ajoutées le temps du test puis supprimées :
test -f server/api/__sitemap__/urls.ts— présentgrep "queryCollection(event, 'blog_fr')"etgrep "queryCollection(event, 'blog_en')"— 1 match chacungrep "'x-default'"— présent (ligne bilingual alternatives)grep "draft.*false"— présent (2 matches, un par locale)pnpm typecheck— 0 erreur surserver/api/__sitemap__/urls.ts(erreur pré-existante surapp/pages/blog/[slug].vue:136ogLocaledu Plan 07-02, hors scope — cf. Deferred Issues)curl http://localhost:3001/api/__sitemap__/urls— retourne JSONSitemapUrl[]valide (2 entrées par article bilingue, alternatives complètes)curl http://localhost:3001/__sitemap__/fr-FR.xml | grep '/fr/blog/_sitemap-smoke'— matchcurl http://localhost:3001/__sitemap__/en-US.xml | grep '/en/blog/_sitemap-smoke'— matchgrep 'hreflang="x-default"' fr-FR.xml— 9 occurrences (8 pages site + 1 article bilingue)grep 'test-kotlin-syntax' sitemap.xml— 0 match (T-07-06 mitigation confirmée : drafts filtrés)
Deferred Issues
Hors scope de ce plan (pre-existing errors) :
app/pages/blog/[slug].vue(136,17): error TS2322—ogLocale: () => (...)type mismatch avecuseSeoMeta'sMaybeFalsy<"fr-FR">. Remonte au Plan 07-02 (useSeoMeta enrichment). Ce fichier n'a pas été modifié par 07-04. À corriger en phase de polish ou plan suivant si non déjà listé.
Known Stubs
Aucun. L'endpoint est pleinement fonctionnel — il retourne [] naturellement quand la seule entrée de contenu est draft (comportement attendu, D-10).
Threat Flags
Aucun nouveau surface de menace. Le plan documentait T-07-06 (IDisclo drafts) — mitigation confirmée : grep test-kotlin-syntax sur le sitemap final renvoie 0 (draft explicitement filtré par .where('draft', '=', false) dans les deux branches).
Self-Check: PASSED
server/api/__sitemap__/urls.ts— FOUND (76 lignes)- Commit
466bed0(feat Task 1) — FOUND in git log (git log --oneline | grep 466bed0) - Endpoint runtime validé via curl (SitemapUrl[] JSON valide, XML final contient les URLs blog + alternates x-default)
- Fixtures de test nettoyées (
content/fr/blog/etcontent/en/blog/ne contiennent quetest-kotlin-syntax.mddraft)