TECHNOLOGIES / ESLint et Prettier

ESLint : la validation de notre code front

ESLint vérifie le fond de notre code JavaScript et Vue.js : erreurs, mauvais usages des directives, conventions d'écriture des composants. La forme est confiée à Prettier, et les types à vue-tsc. Un outil par responsabilité.

Cette fiche documente nos choix de configuration et leur motif, la frontière entre les deux outils, et l'adoption sur un projet existant. Elle est le pendant front de PHP CS Fixer dans notre chaîne de conventions de code.

PRÉSENTATION

Qu'est-ce qu'ESLint ?

ESLint est le linter de référence du monde JavaScript : il lit le code sans l'exécuter et signale ce qui est faux, dangereux ou contraire aux conventions retenues. Chaque règle est activable, configurable, et souvent capable de corriger seule ce qu'elle signale.

Des plugins l'étendent à un langage ou un framework : eslint-plugin-vue comprend les fichiers .vue, leur template et leur script, et porte les règles du style guide officiel.

Chez SmartBooster, ESLint vérifie le fond. La forme, indentation, guillemets, retours à la ligne, ordre des classes Tailwind, est décidée par Prettier, et ESLint a interdiction d'y toucher.

Qu'est-ce qu'ESLint ?

POURQUOI ESLINT

Ce qui en fait notre outil de fond côté front

ESLint tourne sur tout notre code front, projets Symfony avec Vue.js comme projets Vue seuls. C'est une brique de notre démarche qualité, au même titre que PHPStan côté PHP.

Les outils de l'écosystème Vue

ESLint et Prettier sont ceux du dépôt Vue core, ceux que la documentation Vue recommande, et ceux que create-vue, l'outil officiel de création de projet, installe. Suivre create-vue nous aligne sur l'écosystème sans arbitrage de notre part.

Le plugin Vue, au bon niveau

eslint-plugin-vue détecte les mauvais usages des directives et les écarts au style guide. Le preset strongly-recommended ajoute les règles de cohérence que essential laisse de côté : nommage des props et des événements, valeur par défaut des props optionnelles.

Une frontière nette avec Prettier

ESLint sait formater, nous le lui interdisons. eslint-config-prettier, placé en dernier, désactive chaque règle qui touche à la forme, et une commande vérifie qu'il n'en reste aucune. Deux outils qui se contredisent sur un espace, c'est une boucle de correction sans fin.

Une baseline pour l'existant

Les suppressions en masse d'ESLint comptent les violations existantes par fichier et par règle. Seules les nouvelles font échouer le contrôle, et une violation corrigée rend son entrée obsolète. Même principe que la baseline de PHPStan.

TypeScript sans rien reconfigurer

Sur un projet TypeScript, @vue/eslint-config-typescript branche typescript-eslint sur les fichiers .vue, et vue-tsc vérifie les types, hors ESLint. Le typage est une troisième case, il ne déplace pas les deux premières.

Un contrôle mesuré, pas maximal

Le preset recommended, au-dessus du nôtre, a été mesuré sur deux de nos bases de code : 752 violations, dont 84 % sur l'ordre des attributs dans une balise, priorité C du style guide Vue. Réécrire tous les templates pour cela n'apporte rien. Nous l'avons écarté, chiffres à l'appui.

NOTRE USAGE

Comment nous configurons ESLint

Le standard-bundle documente la configuration en couches, sans en copier aucune : chaque projet applique celles qui le concernent. Les extraits ci-dessous en donnent les choix, la documentation du bundle le détail.

Une configuration en couches

Un socle agnostique (@eslint/js recommandé, Prettier, la frontière), puis ce que le contexte ajoute : le plugin Vue, le périmètre assets/ et les globals sur Symfony, deux règles levées sur les pages Inertia, le tri des classes Tailwind, TypeScript. Chaque couche est documentée dans le standard-bundle.

Un verbe corrige, :check rapporte

yarn lint et yarn format corrigent. yarn lint:check et yarn format:check ne font que rapporter. yarn validation enchaîne les deux contrôles, affiche les deux rapports et échoue si l'un des deux échoue : c'est la commande que la CI appelle.

Sur Symfony, le périmètre se restreint

Les commandes ciblent assets/ et les fichiers JS de la racine, jamais vendor/ : un bundle valide son propre code front depuis son propre dépôt. Les globals injectés par Twig, comme route pour FOSJsRouting, sont déclarés pour que no-undef ne les signale pas.

Deux règles levées sur les pages Inertia

Une page Inertia reçoit ses props du contrôleur Symfony et n'est jamais écrite comme une balise. vue/multi-word-component-names et vue/require-default-prop n'y ont pas d'objet, et sont désactivées sur scripts/pages/ et scripts/layout/. Les composants y restent soumis.

PRETTIER

Prettier : la forme, décidée une fois

Prettier formate notre code JavaScript, Vue.js et CSS. Il a peu d'options, nous en touchons quatre, et chacune a son motif. ESLint pourrait formater aussi : nous lui avons retiré ce droit.

Pourquoi Prettier et pas ESLint seul

ESLint peut formater, mais l'écosystème Vue ne le fait pas : Vue core, create-vue, et au-delà React, Tailwind CSS et GitLab confient la forme à Prettier. Il a peu d'options, donc peu à débattre, et le même résultat partout. Sa forme n'est pas la plus belle, elle est la moins discutée.

L'outil écarté : @antfu/eslint-config

Il réunit règles et formatage dans un seul outil, sans Prettier. Nous l'avons écarté : c'est une configuration personnelle, utilisée par aucun dépôt officiel Vue, qui impose ses propres opinions de style et coupe des plugins dédiés, à commencer par le tri des classes Tailwind.

Quatre options, un seul écart

semi: false et printWidth: 100 viennent du template create-vue. singleAttributePerLine applique le style guide Vue. singleQuote: false est notre écart : Prettier choisit le guillemet qui demande le moins d'échappements, et des chaînes françaises pleines d'apostrophes finiraient mélangées.

Le tri des classes Tailwind

prettier-plugin-tailwindcss, le plugin officiel, range les classes utilitaires dans un ordre canonique. Plus aucun débat sur l'endroit où va une classe. Sur Tailwind 4, tailwindStylesheet lui indique le CSS d'entrée, sans quoi il ne connaît pas les utilitaires du design system.

La frontière, vérifiée par une commande

eslint-config-prettier/flat, importé en dernier dans la configuration ESLint, désactive toute règle qui formate, y compris les règles de mise en page du plugin Vue. npx eslint-config-prettier sur un fichier de chaque extension confirme qu'aucune règle en conflit ne subsiste.

Le pragma pour l'existant

Prettier n'a pas de baseline. requirePragma: true le restreint aux fichiers qui commencent par @format : on y passe fichier par fichier, au fil des tickets qui les touchent, et l'option se retire quand tout est formaté.

BONNES PRATIQUES & DOCUMENTATION

Nos conventions ESLint et Prettier

Documentation de référence pour l'équipe SmartBooster.

Le socle

Ce que tout projet front installe, quel que soit son contexte

La configuration est décrite en couches dans docs/front_code_validation.md du standard-bundle. Le socle est agnostique, chaque section suivante ajoute ce qu'un contexte exige.

yarn add --dev eslint @eslint/js eslint-config-prettier prettier

Les scripts du package.json :

"scripts": {
  "lint": "eslint . --fix --cache",
  "lint:check": "eslint .",
  "lint:baseline": "rm -f eslint-suppressions.json && eslint . --suppress-all",
  "format": "prettier --write .",
  "format:check": "prettier --check .",
  "validation": "yarn lint:check; s=$?; yarn format:check && exit $s"
}
  • Un verbe nu corrige, :check ne fait que rapporter. La CI appelle les :check.
  • validation enchaîne les deux contrôles, affiche les deux rapports, et échoue si l'un des deux échoue.
  • --cache écrit un .eslintcache, à ajouter au .gitignore.

prettier.config.mjs :

export default {
  semi: false,
  singleQuote: false,
  printWidth: 100,
  singleAttributePerLine: true,
}

Toute option absente est un défaut de Prettier (tabWidth: 2, trailingComma: "all", ...). Le motif de chacune des quatre est sur cette page, section Prettier.

eslint.config.mjs :

import js from "@eslint/js"
import { defineConfig } from "eslint/config"
import skipFormatting from "eslint-config-prettier/flat"

export default defineConfig([
  { name: "app/files-to-lint", files: ["**/*.{js,mjs}"] },
  js.configs.recommended,
  skipFormatting,
])
  • files déclare les extensions qu'ESLint prend quand on lui passe un répertoire : sans lui, seul .js est analysé. Les couches suivantes y ajoutent .vue et .ts.
  • skipFormatting reste en dernier : sur une règle partagée, la dernière configuration gagne.

La frontière avec Prettier

Comment nous vérifions qu'aucune règle ESLint ne formate

eslint-config-prettier désactive chaque règle ESLint qui doublonne ou contredit Prettier, règles de base et règles de mise en page du plugin Vue comprises. Son CLI vérifie qu'il n'en reste aucune :

npx eslint-config-prettier <fichier.js> <fichier.vue>

Il affiche No rules that are unnecessary or conflict with Prettier were found. quand tout est en ordre. Passer un fichier de chaque extension gérée par la configuration (.js, .vue, .ts) pour que toutes les règles soient comparées.

Nous importons eslint-config-prettier/flat directement, et non @vue/eslint-config-prettier/skip-formatting : le paquet Vue était un paquet de transition, sans maintenance prévue, et create-vue a fait le même changement début 2026 (PR #897).

La couche Vue

Le plugin et le niveau de preset retenu

yarn add --dev eslint-plugin-vue vue-eslint-parser

vue-eslint-parser, qui découpe un fichier .vue pour exposer son template et son script à ESLint, est une dépendance pair de eslint-plugin-vue : avec yarn, elle doit être déclarée explicitement.

import pluginVue from "eslint-plugin-vue"

  { name: "app/files-to-lint", files: ["**/*.{js,mjs,vue}"] },
  // ...
  ...pluginVue.configs["flat/strongly-recommended-error"],

Pourquoi strongly-recommended-error. essential, le défaut de create-vue, n'attrape que les erreurs. strongly-recommended ajoute les règles de cohérence dont l'absence laisse l'écriture des composants dériver :

La variante -error est nécessaire : le preset déclare ces règles en warn, qu'aucune baseline ne retient et qui ne font jamais échouer lint:check.

Ses 12 règles de mise en page ne gênent pas Prettier : 11 sont désactivées par skipFormatting, et vue/first-attribute-linebreak exige ce que Prettier produit déjà.

Pourquoi pas recommended. Il ajoute 8 règles, qui remontent 752 violations mesurées sur deux de nos bases de code. 84 % viennent de vue/attributes-order, une convention d'ordre dans la balise qui obligerait à réécrire tous les templates sans gain fonctionnel, et que le style guide Vue lui-même classe en priorité C. L'essentiel du reste est vue/no-v-html, alors que nos v-html affichent du contenu back-end que nous maîtrisons.

Les couches Symfony, Inertia, Tailwind et TypeScript

Ce que chaque contexte ajoute

Symfony. Les commandes ciblent assets/ et les fichiers JS de la racine, jamais vendor/ : un bundle valide son propre code front depuis son propre dépôt. Le code front tourne dans le navigateur, les fichiers de configuration sous Node, et Twig injecte des globals qu'il faut déclarer :

import globals from "globals"

  {
    name: "app/globals-browser",
    languageOptions: {
      globals: {
        ...globals.browser,
        // Adapter of Ziggy's `route` function to FOSJsRoutingBundle, set on `window` by the Twig layout.
        route: "readonly",
      },
    },
  },
  {
    name: "app/globals-node",
    files: ["**/*.config.{js,mjs}"],
    languageOptions: { globals: { ...globals.node } },
  },

Inertia. Une page et son layout ne sont pas des composants réutilisables : résolus par leur chemin, ils reçoivent leurs props du contrôleur Symfony. Deux règles n'y ont pas d'objet, les composants y restent soumis :

  {
    name: "app/inertia-pages-and-layouts",
    files: ["**/scripts/pages/**/*.vue", "**/scripts/layout/**/*.vue"],
    rules: {
      "vue/multi-word-component-names": "off",
      "vue/require-default-prop": "off",
    },
  },

Tailwind CSS. prettier-plugin-tailwindcss, toujours en dernier dans la liste des plugins Prettier. Sur Tailwind 4, le thème vit dans le CSS : tailwindStylesheet donne au trieur le fichier d'entrée, sans quoi les utilitaires du design system (bg-primary, text-danger) sont rangés parmi les classes inconnues. Une entrée CSS par interface se déclare via overrides.

  plugins: ["prettier-plugin-tailwindcss"],
  tailwindStylesheet: "./assets/styles/app.css",

TypeScript. @vue/eslint-config-typescript branche typescript-eslint sur les fichiers .vue ; defineConfigWithVueTs remplace defineConfig pour propager le parser aux blocs <script>. La vérification des types reste hors ESLint : vue-tsc --build.

import { defineConfigWithVueTs, vueTsConfigs } from "@vue/eslint-config-typescript"

export default defineConfigWithVueTs(
  { name: "app/files-to-lint", files: ["**/*.{ts,mts,vue}"] },
  // ...
  vueTsConfigs.recommended,
  // ...
)

Sur un projet existant

Baseline ESLint, pragma ou commit unique pour Prettier

Les deux outils remontent beaucoup sur une base de code qui ne les a jamais eus. Plutôt qu'un big bang, chacun a sa façon de n'échouer que sur le code nouveau.

ESLint : la baseline. Les suppressions en masse : eslint-suppressions.json compte les violations existantes par fichier et par règle, seules les nouvelles font échouer.

  • yarn lint:baseline régénère le fichier de zéro (--suppress-all ne retire jamais d'entrée). Relire son diff avant de commiter.
  • Violation corrigée → entrée obsolète → lint:check sort en code 2 → régénérer.
  • Seules les règles en error sont enregistrées.

Prettier : le pragma. Pas de baseline, mais requirePragma: true restreint Prettier aux fichiers qui commencent par @format :

  • Faire entrer un fichier : yarn prettier --write --no-require-pragma --insert-pragma <fichiers> (--insert-pragma seul ne fait rien, requirePragma l'emporte).
  • Marqueur : <!-- @format --> dans un .vue, /** @format */ dans un .js ou un .css.
  • Puis yarn lint:baseline : le formatage corrige vue/first-attribute-linebreak, ses entrées deviennent obsolètes.
  • Retirer l'option quand tout est formaté.

Prettier : le commit unique. Formater tout d'un coup est l'autre voie, la seule qui supprime le pragma tout de suite. Elle convient à un projet avec peu de fichiers front, peu de personnes et peu de branches ouvertes : chacune devra être rebasée sur les fichiers reformatés.

  1. Commiter la configuration, fusionner les branches ouvertes.
  2. Un commit avec yarn format, rien d'autre.
  3. Filet : prettier --debug-check sur le même périmètre signale tout fichier dont le sens pourrait changer.
  4. Une fois fusionné (squash ou rebase change le hash), inscrire le hash dans .git-blame-ignore-revs. Chaque développeur configure git config blame.ignoreRevsFile .git-blame-ignore-revs (PhpStorm le suit). GitLab : Blame preferences › Ignore specific revisions.

Pièges : fichiers entiers seulement, aucun outil maintenu ne formate les lignes modifiées ; un texte déplacé sur sa propre ligne dans un template devient un espace de bord une fois compilé par Vue, invisible dans un bloc, visible sous whitespace-pre-wrap ou <pre> ; blame.ignoreRevsFile configuré et fichier absent sur une branche → git blame échoue.

Ensuite. La résorption des suppressions et des fichiers sans pragma entre dans le flux non prioritaire de la maintenance technique, entre deux demandes. Un projet neuf n'a ni baseline ni pragma : les deux contrôles passent dès le premier commit.

FAQ

Les réponses à vos questions

Et si vous ne trouvez pas ce que vous cherchez, nous serons ravis de vous répondre en direct lors d'un rendez-vous entre humains !

Parce que deux outils qui décident de la même chose finissent par se contredire, et qu'un développeur corrige alors l'un pour faire échouer l'autre. Un outil, une responsabilité : un échec nomme son responsable, la forme à Prettier, le fond à ESLint, les types à vue-tsc. Chacun se configure et se met à jour seul. C'est aussi ce que fait l'écosystème Vue, dont nous suivons l'outillage.

create-vue est l'outil officiel de création de projet, maintenu par l'équipe Vue. Ses choix d'outillage sont ceux de l'écosystème, et ils évoluent avec lui : quand create-vue est passé de @vue/eslint-config-prettier à eslint-config-prettier/flat début 2026, nous avons suivi. Nos seuls écarts sont documentés avec leur motif : le niveau du preset Vue, et un guillemet.

essential, le défaut de create-vue, n'attrape que les erreurs. strongly-recommended ajoute les règles de cohérence dont l'absence laisse l'écriture des composants dériver d'un développeur à l'autre : props en camelCase, événements avec tirets, valeur par défaut de chaque prop optionnelle. recommended, au-dessus, a été mesuré : 752 violations sur deux de nos bases de code, 84 % sur l'ordre des attributs, une réécriture de tous les templates sans gain. La variante -error est nécessaire : le preset déclare ses règles en warn, qu'aucune baseline ne retient et qui ne font jamais échouer le contrôle.

ESLint d'abord : yarn lint:baseline génère un fichier de suppressions qui fige les violations existantes, et seules les nouvelles font échouer lint:check. Prettier ensuite, au choix : le pragma @format pour y aller fichier par fichier ou un commit unique qui formate tout, dont le hash est inscrit dans .git-blame-ignore-revs. Le commit unique convient à un projet avec peu de fichiers front et peu de branches ouvertes, parce que chacune devra être rebasée. La résorption des suppressions entre ensuite dans le flux de la maintenance technique.

@vue/eslint-config-typescript, la configuration du template TypeScript de create-vue, branche typescript-eslint sur les fichiers .vue. defineConfigWithVueTs remplace defineConfig pour propager le parser aux blocs script. La vérification des types reste hors ESLint : vue-tsc --build s'en charge. Rien ne change pour Prettier ni pour la frontière entre les deux.

Non. Le bundle documente le standard front, mais sa recette Symfony Flex ne copie aucun fichier JavaScript : les contextes sont trop différents d'un projet à l'autre (Symfony avec Inertia, Vue seul, TypeScript ou non). Chaque projet applique la couche qui le concerne, extraits à l'appui, depuis la documentation du bundle.

Pour aller plus loin

Approfondir votre réflexion

Conventions de code

La chaîne complète, PHP et front, le principe d'un outil par responsabilité et la commande make qa qui les enchaîne.

PHP CS Fixer

Le même partage des rôles côté PHP : la forme à PHP CS Fixer, le fond à PHPStan, et pourquoi PHP_CodeSniffer n'est plus là.

Vue.js

Le framework front de nos interfaces, dont l'outillage officiel dicte les deux outils de cette page.

Vous avez un projet ?

Contactez-nous pour savoir comment nous pouvons vous aider.