fraza-doctor
Diagnostiquer un projet, variables d'environnement manquantes, contrat d'amorçage dérivé, versions incompatibles et vulnérabilités.
fraza-doctor confronte un projet à la version de @fraza/core qu'il a installée. Il ne modifie rien sans --fix, et ce qu'il répare avec --fix ne peut rien détruire.
npx fraza-doctorOptions
| Option | Effet |
|---|---|
| (aucune) | Diagnostic seul. |
--fix | Applique les réparations sûres, puis re-diagnostique. |
--json | Sort les constats en JSON. |
--no-audit | Saute npm audit, qui est l'étape la plus lente. |
help | Affiche l'aide. |
Code de sortie 0 si aucun constat n'est bloquant, 1 sinon. Un avertissement ne bloque pas.
Les quatre vérifications
Variables d'environnement
Le catalogue de référence est l'union de deux déclarations : les variables que le core déclare dans src/core/doctor/env.js, et les envVars déclarées au manifeste de chaque module installé.
Il ne se lit pas dans .env.example. Ce fichier est le modèle de démarrage copié par @fraza/start, donc réduit à ce qu'un nouveau projet doit remplir : le prendre pour une déclaration revenait à dénoncer comme inconnue toute variable que le core lit sans qu'elle figure au modèle.
Le doctor lit les fichiers d'environnement du mode courant, dans l'ordre de priorité de Next.js : .env.development.local, .env.local, .env.development, puis .env en développement. La première valeur trouvée pour une variable est celle qu'il retient, comme l'application. --fix n'écrit que dans le .env.
Les modules sont lus qu'ils soient activés ou non. C'est délibéré : un FRAZA_MODULE_<NOM> manquant est précisément ce qu'il faut signaler, et filtrer sur les modules actifs le cacherait.
Un manifeste peut déclarer un gabarit plutôt qu'une variable, FRAZA_MODULE_POSTS_TYPE_<KEY> par exemple. Son absence n'est jamais signalée, puisque le nom réel dépend du projet, mais il sert à reconnaître ses instances : FRAZA_MODULE_POSTS_TYPE_BLOGUE_LIST est une variable connue, pas une inconnue.
| Constat | Gravité |
|---|---|
| Variable absente et requise | Bloquant |
| Variable absente et optionnelle | Jamais signalée |
| Variable présente mais vide alors qu'elle est requise | Bloquant |
Variable requise déjà proposée par un --fix, encore commentée | Bloquant |
Variable FRAZA_* inconnue du core et des modules | Information |
Trois variables du core sont requises, et leur absence est donc bloquante : FRAZA_DATABASE_URL, FRAZA_ENCRYPTION_KEY et NEXT_PUBLIC_URL. Le stockage, le courriel et les paiements restent optionnels : un projet sans téléversement, sans courriel transactionnel ou sans paiement est une configuration légitime.
Une variable optionnelle absente n'est pas une dérive. Sa valeur par défaut s'applique, le projet fonctionne, et le développeur n'a rien à faire. Un module de bonne taille en déclare trente, dont trois comptent vraiment : les signaler toutes noyait les trois. Le catalogue les garde malgré tout, puisque c'est lui qui distingue une clé connue d'une clé inconnue.
Corollaire assumé : une variable optionnelle ajoutée par une version ultérieure du core ou d'un module n'apparaît plus au diagnostic. C'est le CHANGELOG du paquet qui l'annonce, et fraza-update qui le montre à l'installation.
Contrat de projet
Le contrat déclare les fichiers qu'un projet doit contenir pour que cette version du core démarre. Il vit dans le core, pas dans @fraza/start : c'est le core qui change quand une sous-entrée bouge ou que l'ordre d'amorçage évolue, et un projet généré ne garde aucune trace de la version du starter qui l'a produit.
Deux modes de vérification :
| Mode | Fichiers | Comportement |
|---|---|---|
| Exact | Les réexports d'une ligne, les redirections fixes et le layout de l'administration. | Aucune personnalisation n'a de sens. Un écart est bloquant. |
| Fragments | instrumentation.js, app/layout.js, theme.js, jsconfig.json, next.config.mjs, styles/globals.css. | Présence des fragments obligatoires vérifiée, jamais le contenu entier. Un fichier existant n'est jamais réécrit en entier : seuls ses correctifs ciblés, quand il en a, le modifient. |
jsconfig.json relève des fragments : un projet ajoute légitimement ses propres alias, seuls @styles et @components sont exigés parce que les gabarits du core en dépendent. La recherche d'un fragment ignore les blancs, donc un fichier repassé au formateur reste conforme.
Une entrée peut ne concerner qu'une partie des projets. next.config.mjs n'est vérifié que si le package.json déclare @tailwindcss/turbopack : sous Turbopack, create-next-app branche Tailwind par une règle turbopack.rules de ce fichier, sans postcss.config.mjs. Les versions de @fraza/start antérieures écrasaient ce fichier par le leur, et le site démarrait sans aucun style. Un projet sous @tailwindcss/postcss n'est pas concerné.
styles/globals.css relève aussi des fragments : la feuille appartient au projet, seul l'import de ../app/.fraza/sources.css est exigé. Ce fichier, généré par fraza-modules sync, porte un @source par module activé. Sans lui, un module nommé hors des scopes @fraza et @fraza-pro, ou installé par lien, s'affiche sans style. Voir Styles.
Un fichier Exact se compare sans tenir compte de l'indentation ni des fins de ligne : le layout de l'administration, qui fait plusieurs lignes, reste conforme une fois repassé au formateur du projet. Il n'est réécrit par --fix que s'il est absent ou s'il porte encore, à cette tolérance près, le contenu d'une version antérieure du contrat. C'est ce qui permet de remonter un projet existant sans rien détruire : app/fraza/[...path]/page.js, par exemple, a gagné l'import de app/.fraza/public-pages.server.js, et un projet resté au réexport d'une ligne est corrigé automatiquement. Un contenu inconnu, lui, est signalé et laissé intact.
Quand un fichier ainsi réécrit ou corrigé référence un manifeste de app/.fraza/ qui n'existe pas encore, sources.css compris, la commande le dit et renvoie à npx fraza-modules sync. Elle ne génère jamais ces fichiers elle-même : ils appartiennent à fraza-modules, et les écrire ici écraserait un manifeste peuplé par une version vide.
Une entrée en fragments peut aussi lister des fragments retirés : ceux qu'une version antérieure du contrat exigeait et que celle-ci ne veut plus voir. Leur présence n'empêche pas le projet de démarrer, le constat est donc un avertissement. Les commentaires sont ignorés. C'est le cas des manifestes clients dans app/layout.js : ils se rendent désormais dans app/(admin)/admin/layout.js, et les garder à la racine fait télécharger le code de l'admin à chaque page publique.
La commande contrôle aussi que postinstall, dev et build lancent bien fraza-modules sync.
Correctifs ciblés
Un fichier en fragments mêle le contrat et le code du projet : le réécrire perdrait ce code. Quand un changement du core suit un motif connu, l'entrée du contrat déclare un correctif ciblé, dans src/core/doctor/patches.js. Il ne touche que les lignes qu'il reconnaît, là où elles sont, et garde le reste au caractère près : commentaires, imports ajoutés, mise en forme.
Trois garde-fous le rendent sûr :
- Il renonce dans le doute. Deux appels à
initializeFraza, une variablethemedéjà déclarée, unTHEME_INIT_SCRIPTutilisé ailleurs que dans la balise attendue : le correctif ne devine pas, le constat reste à corriger à la main avec la liste des fragments manquants. - Le résultat est revérifié avant l'écriture. Si un fragment manque encore après correction, ou qu'un fragment retiré est toujours là, rien n'est écrit : un fichier à moitié réparé serait pire qu'un constat.
- Il est rejouable. Un fichier déjà corrigé ressort inchangé.
Le diagnostic annonce le correctif avant de l'appliquer, et --fix distingue ce qu'il a créé, réécrit ou corrigé sur place.
| Correctif | Fichier | Effet |
|---|---|---|
instrumentation-theme | instrumentation.js | Ajoute l'import de ./theme.js au-dessus de l'appel à initializeFraza, et y ajoute theme. Un appel étalé sur plusieurs lignes est remis sur une ligne. |
layout-theme-style | app/layout.js | Remplace la balise <script> de THEME_INIT_SCRIPT par <ThemeStyle />, ou l'ajoute en tête du <head>. Remplace l'import, en gardant les autres noms importés de @fraza/core/themes. |
layout-admin-manifests | app/layout.js | Retire l'import et le rendu de modules.client.js et de blocks.client.js, quatre lignes en tout. Renonce si un des deux composants sert ailleurs que seul sur sa ligne. |
globals-module-sources | styles/globals.css | Ajoute @import '../app/.fraza/sources.css'; juste après l'import de @fraza/core/styles/core.css, avec les mêmes guillemets. Renonce si cet import n'est pas présent une seule fois, seul sur sa ligne. |
next-config-tailwind | next.config.mjs | Ajoute la règle turbopack.rules qui confie les *.css à @tailwindcss/turbopack, en tête de l'objet ouvert par const nextConfig = {. Renonce si une clé turbopack existe déjà ou si l'objet a une autre forme. |
Le retrait des manifestes clients attend le layout de l'administration : tant que app/(admin)/admin/layout.js porte un contenu inconnu, que --fix ne réécrira pas, ce correctif-là ne s'applique pas. Les retirer quand même laisserait l'admin sans pages de module ni blocs personnalisés. Les autres correctifs du fichier ne sont pas retenus pour autant : sur un projet antérieur au thème, <ThemeStyle /> est posé et les manifestes restent en place. Le constat indique alors de les retirer à la main une fois le layout de l'administration corrigé, et le passage suivant de --fix s'en charge si ce layout est devenu conforme.
Le thème passe ainsi par trois fragments : instrumentation.js importe ./theme.js et appelle initializeFraza({ modules, theme }), app/layout.js rend <ThemeStyle />, et theme.js exporte un defineTheme(...). Sur un projet antérieur au thème, npx fraza-doctor --fix fait les trois : il corrige les deux premiers sur place et crée theme.js avec les valeurs par défaut. Voir Personnaliser l'apparence.
Versions et compatibilité
Confronte le core installé au requiresCore de chaque module, signale un manifeste dont la version diverge de son package.json, et compare les paquets tiers aux bornes de paquets supportés.
Vérifie aussi les requiresModules des modules activés : un module requis absent ou désactivé, ou un cycle entre modules, est bloquant. Le démarrage lève sur les mêmes constats. Voir Dépendre d'un autre module.
Le CLI est strict là où l'amorçage ne peut pas se le permettre : initializeFraza se contente d'avertir sur une combinaison non testée, parce qu'une exception y mettrait un site de production à terre. Ici tout constat de compatibilité est bloquant.
Vulnérabilités
Lance npm audit --omit=dev et remonte chaque alerte jusqu'au paquet de premier niveau qui la tire. Si ce paquet figure dans supported.json et qu'il est sous la version validée, le remède affiché est npm update <paquet>. Ce n'est que lorsqu'il est déjà à jour que la piste overrides est proposée.
C'est l'automatisation de la règle décrite dans Sécurité : la plupart des alertes d'un site Fraza viennent de l'arbre de Next.js et disparaissent avec une montée de version.
Ce que --fix répare, et ce qu'il ne touche pas
| Situation | Action de --fix |
|---|---|
Variable requise absente du .env | Ajoutée en fin de fichier, commentée, avec sa description. |
| Fichier de contrat exact absent | Écrit avec le contenu attendu. |
| Fichier de contrat exact resté à une version antérieure du core | Remonté à la version courante. |
| Fichier de contrat exact au contenu inconnu | Laissé intact, signalé avec le contenu attendu. |
| Fichier à fragments absent, quand le contrat en porte un contenu de semis | Écrit avec ce contenu, jsconfig.json ou theme.js par exemple. |
| Fichier à fragments incomplet, reconnu par son correctif ciblé | Corrigé sur place : seules les lignes reconnues changent. Voir Correctifs ciblés. |
| Fichier à fragments incomplet, non reconnu ou sans correctif | Laissé intact, les fragments manquants sont affichés. |
| Fichier à fragments qui porte encore un fragment retiré, reconnu par son correctif ciblé | Corrigé sur place : seules les lignes du fragment sont retirées. |
| Fichier à fragments qui porte encore un fragment retiré, non reconnu | Laissé intact, les fragments à retirer sont affichés. |
instrumentation.js, app/layout.js, next.config.mjs, styles/globals.css | Jamais réécrits en entier. Ils mêlent contrat et code du projet, seuls leurs correctifs ciblés les modifient. |
Valeur existante dans le .env | Jamais modifiée. Aucune ligne n'est jamais supprimée. |
Les variables sont ajoutées commentées, donc --fix est incapable de changer le comportement du projet. Le développeur décommente ce qui le concerne. Un second --fix ne les rajoute pas : la commande reconnaît ce qu'elle a déjà proposé.
Sortie
◆ Diagnostic du projet...
✓ Variables d'environnement : rien à signaler
◆ Contrat de projet : 2 constat(s)
✗ app/fraza/media/[filename]/route.js est absent. Route de service des fichiers.
⚠ Le script "dev" ne lance pas « fraza-modules sync ».
✓ Versions et compatibilité : rien à signaler
✓ Vulnérabilités : rien à signaler
✗ 1 constat(s) bloquant(s), 1 avertissement(s)
· 1 réparable(s) : lance « npx fraza-doctor --fix »Notes
Un projet fraîchement généré est déclaré conforme. C'est la condition de crédibilité de l'outil, et elle est vérifiée à la publication de @fraza/start par son script check:contract.
Le diagnostic ne demande aucune base de données. Seules les vérifications de migration passent par la base, et elles vivent dans fraza-db.
npm audit est lancé sans shell, de la même façon que les lectures de fraza-update, Windows compris. Si npm reste introuvable, la vérification rapporte « npm audit n'a rien retourné d'exploitable » au lieu d'échouer.
--fix re-diagnostique après réparation, pour que le code de sortie reflète l'état final du projet et non celui d'avant les corrections.