[{"data":1,"prerenderedAt":6},["ShallowReactive",2],{"article:fr:nuxt-cloudflare-pages":3},{"html":4,"lang":5},"\u003Ch2>Un portfolio servi depuis le réseau de Cloudflare\u003C\u002Fh2>\n\u003Cp>Ce site est une application \u003Cstrong>Nuxt 3\u003C\u002Fstrong> dont toutes les pages sont pré-rendues au build : l'accueil, les pages de CV et chacun des articles du blog. Cloudflare Pages est un hébergement idéal pour ce cas : HTML servi depuis le réseau mondial de Cloudflare, HTTPS automatique, un déploiement à chaque push, et un aperçu par branche. Voici la configuration, et surtout les trois pièges rencontrés lors de la dernière refonte.\u003C\u002Fp>\n\n\u003Ch3>La configuration du build\u003C\u002Fh3>\n\u003Cp>Dans le tableau de bord (\u003Cem>Workers et Pages → votre projet → Paramètres → Build\u003C\u002Fem>) :\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>Commande de build\u003C\u002Fstrong> : \u003Ccode>npm run build\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Répertoire de sortie\u003C\u002Fstrong> : \u003Ccode>dist\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Branche de production\u003C\u002Fstrong> : \u003Ccode>master\u003C\u002Fcode> (ou \u003Ccode>main\u003C\u002Fcode>)\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>Nuxt détecte automatiquement l'environnement Cloudflare Pages et utilise le preset Nitro \u003Ccode>cloudflare-pages\u003C\u002Fcode> : les routes déclarées au prérendu deviennent des fichiers HTML statiques dans \u003Ccode>dist\u002F\u003C\u002Fcode>, et le reste est servi par un Worker. Pour un site 100 % statique, déclarez les routes à pré-rendre dans \u003Ccode>nuxt.config.ts\u003C\u002Fcode>. Les générer depuis vos données évite d'oublier un article :\u003C\u002Fp>\n\u003Cpre>\u003Ccode>import { blogArticles } from '.\u002Fdata\u002Fblog'\n\nconst staticPages = ['\u002F', '\u002Fexperience', '\u002Fskills', '\u002Fprojects', '\u002Feducation', '\u002Fblog']\nconst blogPages = blogArticles.map(article =&gt; `\u002Fblog\u002F${article.slug}`)\n\nexport default defineNuxtConfig({\n  nitro: {\n    prerender: {\n      routes: [...staticPages, ...blogPages],\n      crawlLinks: true,\n    },\n  },\n})\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Le même tableau alimente le sitemap : un nouvel article est automatiquement pré-rendu et référencé, sans rien toucher d'autre.\u003C\u002Fp>\n\n\u003Ch3>Piège n°1 : la branche de production\u003C\u002Fh3>\n\u003Cp>Après la refonte, poussée sur \u003Ccode>master\u003C\u002Fcode>, le site en ligne n'avait pas changé. Le build avait pourtant réussi. En réalité, la branche de production du projet était restée sur une ancienne branche de travail : chaque push sur \u003Ccode>master\u003C\u002Fcode> ne produisait qu'un \u003Cstrong>déploiement d'aperçu\u003C\u002Fstrong>, sur une URL \u003Ccode>*.pages.dev\u003C\u002Fcode>, sans toucher au domaine principal.\u003C\u002Fp>\n\u003Cp>À vérifier dans \u003Cem>Paramètres → Build → Contrôle de branche\u003C\u002Fem>. Et changer la branche de production ne redéploie rien : il faut un nouveau commit sur cette branche (ou relancer un déploiement) pour que la production se mette à jour.\u003C\u002Fp>\n\n\u003Ch3>Piège n°2 : la version de Node\u003C\u002Fh3>\n\u003Cp>Nuxt 3.21, Vite 7 et Nitro exigent \u003Cstrong>Node \u003Ccode>^20.19\u003C\u002Fcode> ou \u003Ccode>&gt;=22.12\u003C\u002Fcode>\u003C\u002Fstrong>. Le système de build de Cloudflare lit la version dans un fichier \u003Ccode>.node-version\u003C\u002Fcode> ou \u003Ccode>.nvmrc\u003C\u002Fcode> à la racine du dépôt (ou dans la variable d'environnement \u003Ccode>NODE_VERSION\u003C\u002Fcode>). Un simple \u003Ccode>20\u003C\u002Fcode> est ambigu : fixez une version majeure récente, clairement compatible.\u003C\u002Fp>\n\u003Cpre>\u003Ccode>echo 22 &gt; .node-version\necho 22 &gt; .nvmrc\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Ch3>Piège n°3 : le lockfile et \u003Ccode>npm ci\u003C\u002Fcode>\u003C\u002Fh3>\n\u003Cp>Le build échouait en quelques secondes avec ce message :\u003C\u002Fp>\n\u003Cpre>\u003Ccode>npm error `npm ci` can only install packages when your package.json and\npackage-lock.json or npm-shrinkwrap.json are in sync.\nnpm error Missing: oxc-parser@0.151.0 from lock file\nnpm error Missing: esbuild@0.28.2 from lock file\n...\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Pourtant, \u003Ccode>npm install\u003C\u002Fcode> et le build passaient parfaitement en local. La cause : le \u003Ccode>package-lock.json\u003C\u002Fcode> avait été généré avec \u003Cstrong>npm 11\u003C\u002Fstrong> (livré avec Node 24), alors que Cloudflare installe les dépendances avec \u003Ccode>npm ci\u003C\u002Fcode> en \u003Cstrong>npm 10\u003C\u002Fstrong>. Les deux versions ne résolvent pas les dépendances optionnelles de la même façon, et npm 10 considérait le lockfile comme désynchronisé.\u003C\u002Fp>\n\u003Cp>La correction : régénérer le lockfile avec la même version de npm que la CI, déclarée dans le champ \u003Ccode>packageManager\u003C\u002Fcode> du \u003Ccode>package.json\u003C\u002Fcode> :\u003C\u002Fp>\n\u003Cpre>\u003Ccode>npx npm@10.9.4 install --package-lock-only\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Et surtout, \u003Cstrong>reproduire la CI en local\u003C\u002Fstrong> avant de pousser, sur une copie propre du dépôt :\u003C\u002Fp>\n\u003Cpre>\u003Ccode>git clone . \u002Ftmp\u002Fci-check &amp;&amp; cd \u002Ftmp\u002Fci-check\nnpx npm@10.9.4 ci\nnpm run build\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Si ces deux commandes passent, le build Cloudflare passera aussi.\u003C\u002Fp>\n\n\u003Ch3>Les redirections avec slash final\u003C\u002Fh3>\n\u003Cp>Une fois en ligne, \u003Ccode>curl -I https:\u002F\u002Fbenmacha.tn\u002Fexperience\u003C\u002Fcode> renvoie un \u003Cstrong>308\u003C\u002Fstrong> vers \u003Ccode>\u002Fexperience\u002F\u003C\u002Fcode>. Ce n'est pas une erreur : chaque page pré-rendue est un fichier \u003Ccode>experience\u002Findex.html\u003C\u002Fcode>, et Cloudflare Pages redirige vers l'URL de répertoire. Pour le SEO, gardez des liens internes et des URL canoniques cohérents avec ce comportement, pour éviter une redirection à chaque clic.\u003C\u002Fp>\n\n\u003Ch3>Suivre un déploiement sans ouvrir le tableau de bord\u003C\u002Fh3>\n\u003Cp>Cloudflare publie l'état de chaque build sur GitHub, sous forme de \u003Cem>check run\u003C\u002Fem> attaché au commit. Avec la CLI GitHub, on suit le déploiement depuis le terminal :\u003C\u002Fp>\n\u003Cpre>\u003Ccode>gh api repos\u002FMOI\u002FMON-REPO\u002Fcommits\u002F$(git rev-parse HEAD)\u002Fcheck-runs \\\n  --jq '.check_runs[] | select(.name==\"Cloudflare Pages\") | \"\\(.status) \\(.conclusion)\"'\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Le résultat passe de \u003Ccode>in_progress\u003C\u002Fcode> à \u003Ccode>completed success\u003C\u002Fcode>, ou \u003Ccode>completed failure\u003C\u002Fcode>. Dans ce dernier cas, le lien \u003Ccode>details_url\u003C\u002Fcode> du check mène directement aux logs du build.\u003C\u002Fp>\n\n\u003Ch3>En résumé\u003C\u002Fh3>\n\u003Cul>\n\u003Cli>Vérifiez que la \u003Cstrong>branche de production\u003C\u002Fstrong> est bien celle sur laquelle vous poussez\u003C\u002Fli>\n\u003Cli>Fixez la \u003Cstrong>version de Node\u003C\u002Fstrong> dans \u003Ccode>.node-version\u003C\u002Fcode>, compatible avec vos dépendances\u003C\u002Fli>\n\u003Cli>Générez le \u003Cstrong>lockfile\u003C\u002Fstrong> avec la même version de npm que la CI, et testez \u003Ccode>npm ci\u003C\u002Fcode> en local\u003C\u002Fli>\n\u003Cli>Générez les routes pré-rendues et le sitemap \u003Cstrong>depuis vos données\u003C\u002Fstrong>\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>Une fois ces points réglés, le cycle devient idéal : un \u003Ccode>git push\u003C\u002Fcode>, une minute de build, et le site est à jour partout dans le monde.\u003C\u002Fp>\n","fr",1790543096409]