Files
kayjaydee 6762990e61 docs(05): add detailed pattern map for @nuxt/content setup and renderer
- Introduced a new document outlining the configuration and component patterns for integrating @nuxt/content.
- Included mappings for `nuxt.config.ts`, `content.config.ts`, and new components `ProseImg.vue` and `Alert.vue`.
- Added example markdown content for testing syntax highlighting and layout.
- Documented critical patterns and anti-patterns to follow during implementation.
2026-04-21 12:52:59 +02:00

373 lines
9.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Phase 5: @nuxt/content Setup & Renderer — Pattern Map
**Mapped:** 2026-04-21
**Files analyzed:** 7 (2 modifications + 5 créations)
**Analogs found:** 5 / 7
---
## File Classification
| New/Modified File | Role | Data Flow | Closest Analog | Match Quality |
|---|---|---|---|---|
| `nuxt.config.ts` | config | — | `nuxt.config.ts` (lui-même, existant) | exact — extension |
| `content.config.ts` | config | CRUD | `nuxt.config.ts` (structure `defineNuxtConfig`) | role-match |
| `app/assets/css/main.css` | config | — | `app/assets/css/main.css` (lui-même, existant) | exact — extension |
| `app/components/content/ProseImg.vue` | component | request-response | `app/components/ProjectCard.vue` (NuxtImg + Props interface) | role-match |
| `app/components/content/Alert.vue` | component | request-response | `app/components/TechBadge.vue` (withDefaults + UBadge + computed map) | role-match |
| `content/fr/blog/test-kotlin-syntax.md` | content | — | aucun | no-analog |
| `content/en/blog/test-kotlin-syntax.md` | content | — | aucun | no-analog |
---
## Pattern Assignments
### `nuxt.config.ts` (config — extension)
**Analog:** `nuxt.config.ts` lui-même (ligne 165)
**État actuel** (lignes 714) :
```typescript
modules: [
'@nuxt/ui',
'@nuxtjs/i18n',
'@nuxt/eslint',
'@nuxtjs/sitemap',
'nuxt-gtag',
'@nuxt/image'
],
```
**Pattern à ajouter — ajout dans `modules`** :
```typescript
modules: [
'@nuxt/ui',
'@nuxtjs/i18n',
'@nuxt/eslint',
'@nuxtjs/sitemap',
'nuxt-gtag',
'@nuxt/image',
'@nuxt/content' // ← ajouter ici
],
```
**Pattern à ajouter — bloc `content` après les modules existants** :
```typescript
content: {
build: {
markdown: {
highlight: {
theme: {
default: 'github-light',
dark: 'github-dark'
},
langs: ['kotlin', 'java', 'typescript', 'shell', 'bash', 'json', 'vue', 'html', 'css']
}
}
},
experimental: {
sqliteConnector: 'native' // Node 22+ — pas de better-sqlite3
}
},
```
**Contexte critique :** `colorMode.classSuffix: ''` est déjà configuré ligne 29 — Shiki dual-theme fonctionne via `html.dark`, donc compatible sans modification.
---
### `content.config.ts` (config — création, racine du projet)
**Analog:** Structure `nuxt.config.ts` (pattern `defineNuxtConfig``defineContentConfig`)
**Pattern complet** (source: RESEARCH.md Pattern 2) :
```typescript
import { defineContentConfig, defineCollection, z } from '@nuxt/content'
const blogSchema = z.object({
title: z.string(),
description: z.string(),
date: z.string(),
tags: z.array(z.string()).optional(),
image: z.string().optional(),
})
export default defineContentConfig({
collections: {
blog_fr: defineCollection({
type: 'page',
source: { include: 'fr/blog/**/*.md', prefix: '/blog' },
schema: blogSchema,
}),
blog_en: defineCollection({
type: 'page',
source: { include: 'en/blog/**/*.md', prefix: '/en/blog' },
schema: blogSchema,
}),
},
})
```
**Note sur le prefix :** `i18n.strategy: 'prefix_except_default'` avec `defaultLocale: 'fr'` → les articles FR sont sous `/blog/slug`, les EN sous `/en/blog/slug`. (Assumption A3 de RESEARCH.md — valider avec l'article de test.)
---
### `app/assets/css/main.css` (config — extension)
**Analog:** `app/assets/css/main.css` lui-même (lignes 13, existant)
**État actuel** (lignes 13) :
```css
@import "tailwindcss";
@import "@nuxt/ui";
```
**Pattern à ajouter — une ligne après `@import "@nuxt/ui"`** :
```css
@import "tailwindcss";
@import "@nuxt/ui";
@plugin "@tailwindcss/typography"; /* ← ajouter ici — syntaxe Tailwind v4 obligatoire */
```
**Anti-pattern à éviter :** Ne pas utiliser `plugins: [require('@tailwindcss/typography')]` dans `tailwind.config.js` — ignoré en Tailwind v4.
---
### `app/components/content/ProseImg.vue` (component, request-response)
**Analog:** `app/components/ProjectCard.vue` — utilisation de `NuxtImg` avec Props interface (lignes 116, 2535)
**Imports pattern** (depuis ProjectCard.vue, lignes 13) :
```vue
<script setup lang="ts">
// Pas d'import externe — NuxtImg est auto-importé par @nuxt/image
```
**Props pattern** (depuis ProjectCard.vue, lignes 48) :
```typescript
interface Props {
project: Project
}
const props = defineProps<Props>()
```
**NuxtImg pattern** (depuis ProjectCard.vue, lignes 2635) :
```vue
<NuxtImg
:src="project.image"
:alt="`...`"
loading="lazy"
format="webp"
width="400"
height="300"
class="w-full h-52 object-cover"
/>
```
**Pattern cible pour ProseImg.vue** (adapté avec `withDefaults` — depuis TechBadge.vue ligne 11) :
```vue
<script setup lang="ts">
interface Props {
src: string
alt?: string
title?: string
width?: string | number
height?: string | number
}
const props = withDefaults(defineProps<Props>(), {
alt: '',
})
</script>
<template>
<NuxtImg
:src="props.src"
:alt="props.alt"
:title="props.title"
:width="props.width"
:height="props.height"
class="rounded-lg w-full"
sizes="sm:600px md:800px lg:1000px"
/>
</template>
```
---
### `app/components/content/Alert.vue` (component, request-response)
**Analog:** `app/components/TechBadge.vue``withDefaults` + computed map de valeurs + composant Nuxt UI (`UBadge`) (lignes 157)
**withDefaults pattern** (depuis TechBadge.vue, lignes 1113) :
```typescript
const props = withDefaults(defineProps<Props>(), {
showLevel: true,
showImage: true,
})
```
**Computed map pattern** (depuis TechBadge.vue, lignes 4456) :
```typescript
const levelColor = computed(() => {
switch (techData.value.level) {
case 'Advanced': return 'success' as const
case 'Intermediate': return 'primary' as const
default: return 'neutral' as const
}
})
```
**Pattern cible pour Alert.vue** (adapté avec `UAlert` Nuxt UI + `ContentSlot` MDC) :
```vue
<script setup lang="ts">
interface Props {
type?: 'info' | 'warning' | 'tip' | 'danger'
}
const props = withDefaults(defineProps<Props>(), {
type: 'info',
})
const iconMap = {
info: 'i-heroicons-information-circle',
warning: 'i-heroicons-exclamation-triangle',
tip: 'i-heroicons-light-bulb',
danger: 'i-heroicons-x-circle',
}
const colorMap = {
info: 'info',
warning: 'warning',
tip: 'success',
danger: 'error',
}
</script>
<template>
<UAlert
:icon="iconMap[props.type]"
:color="colorMap[props.type] as any"
variant="soft"
class="my-4"
>
<template #description>
<ContentSlot :use="$slots.default" unwrap="p" />
</template>
</UAlert>
</template>
```
**Point critique :** `<ContentSlot :use="$slots.default" unwrap="p" />` est obligatoire — sans lui le contenu entre `::alert` et `::` n'est pas rendu (Pitfall 4 RESEARCH.md).
---
### `content/fr/blog/test-kotlin-syntax.md` (content — création)
**Analog:** aucun (pas de fichiers markdown dans le projet actuellement)
**Pattern depuis RESEARCH.md (Code Examples)** :
```markdown
---
title: "Test Kotlin Syntax Highlighting"
description: "Article de test pour valider le renderer"
date: "2026-04-21"
tags: ["kotlin", "hytale", "test"]
---
## Bloc de code Kotlin
```kotlin
fun main() {
println("Hello, Hytale!")
}
```
## Image optimisée
![Test image](/images/test.png)
## Tableau
| Colonne A | Colonne B |
|-----------|-----------|
| Valeur 1 | Valeur 2 |
## Callout
::alert{type="info"}
Ceci est un callout d'information.
::
```
**Critère de validation :** Ce fichier doit couvrir les 4 success criteria : bloc Kotlin coloré (BLOG-04), image via ProseImg (BLOG-05), tableau, callout (BLOG-01).
---
### `content/en/blog/test-kotlin-syntax.md` (content — création)
**Analog:** même structure que la version FR, même slug, contenu traduit en anglais.
---
## Shared Patterns
### Props avec valeurs par défaut (withDefaults)
**Source:** `app/components/TechBadge.vue` lignes 1114
**Apply to:** `ProseImg.vue`, `Alert.vue`
```typescript
const props = withDefaults(defineProps<Props>(), {
// valeurs par défaut pour props optionnelles
})
```
### NuxtImg usage
**Source:** `app/components/ProjectCard.vue` lignes 2635, `app/components/TechBadge.vue` lignes 6574
**Apply to:** `ProseImg.vue`
- Toujours utiliser `:src`, `:alt`, `loading="lazy"` au minimum
- `format="webp"` si format fixe, sinon laisser @nuxt/image décider
- `sizes` pour responsive
### Composants Nuxt UI (UAlert, UBadge)
**Source:** `app/components/TechBadge.vue` ligne 76, `app/components/FAQSection.vue` ligne 33
**Apply to:** `Alert.vue`
- Nuxt UI est auto-importé — pas d'import explicite nécessaire
- Utiliser `color` + `variant` pour le style
- `as any` acceptable pour les types union non-exhaustifs de Nuxt UI
### Convention import types
**Source:** `app/components/ProjectCard.vue` ligne 2
**Apply to:** `content.config.ts`
```typescript
import type { ... } from '~~/shared/types' // types partagés
import { ... } from '@nuxt/content' // imports de librairie
```
### Auto-import composants
**Source:** `nuxt.config.ts` lignes 1519
**Apply to:** `ProseImg.vue`, `Alert.vue`
```typescript
components: [
{
path: '~/components',
pathPrefix: false, // → components/content/Alert.vue est auto-importé
},
],
```
Les composants dans `components/content/` sont auto-importés par Nuxt ET reconnus par `@nuxt/content` comme Prose overrides / composants MDC.
---
## No Analog Found
| File | Role | Data Flow | Reason |
|---|---|---|---|
| `content/fr/blog/test-kotlin-syntax.md` | content | — | Pas de fichiers markdown dans le projet — nouveau format |
| `content/en/blog/test-kotlin-syntax.md` | content | — | Pas de fichiers markdown dans le projet — nouveau format |
Le planner doit utiliser le pattern RESEARCH.md "Code Examples" pour ces deux fichiers.
---
## Metadata
**Analog search scope:** `app/components/`, `app/assets/css/`, `nuxt.config.ts`
**Files scanned:** 6 (nuxt.config.ts, main.css, ProjectCard.vue, TechBadge.vue, FAQSection.vue, app.vue partiel)
**Pattern extraction date:** 2026-04-21