Publié par

Il y a 4 semaines -

Temps de lecture 8 minutes

La base de code de vos applications Angular toujours bien formatée avec Prettier

D’un projet à l’autre, les règles de formatage de code peuvent varier sensiblement et il n’est pas toujours facile de les suivre à la lettre. Même au sein de petites équipes, vous avez sans doute déjà rencontré du code au formatage non homogène, ce qui n’en facilite pas la compréhension. Alors, pour favoriser un formatage particulier au sein de votre équipe, vous pouvez écrire un long guide de contribution, mais il vous faudra dépenser pas mal d’énergie pour le promouvoir et plus encore pour le faire respecter.

Au lieu de cela, je vous propose de mettre en place un outil de formatage de code automatisable très populaire : Prettier. À la fin de ce tutoriel, vous saurez comment intégrer Prettier dans vos applications Angular, pour qu’il fonctionne harmonieusement avec TSLint. Et vous serez en mesure de formater le code à partir de la ligne de commande, depuis votre IDE pendant que vous développez, mais aussi juste avant de pousser vos précieuses modifications sur votre gestionnaire de sources, Git.

Prérequis

Je suppose que vous avez déjà installé Node.js et Angular CLI sur votre machine. Ensuite, vous pouvez partir d’une application existante (à condition de l’avoir généré avec Angular CLI) ou en créer une nouvelle de la manière suivante :

$ ng new angular-prettier
$ cd angular-prettier

Mais TSLint ne fait-il pas déjà le job ?

TSLint fait partie de l’outillage de toute application Angular générée avec Angular CLI. Pour rappel, TSLint est un outil d’analyse statique, qui définie des règles permettant d’augmenter la qualité du code TypeScript en termes de lisibilité, maintenabilité et fonctionnalité.

Voici un exemple de règle pour chaque catégorie :

  • Limiter le nombre maximal de caractères par ligne (max-line-length) permet de rendre le code plus lisible.
  • Limiter le nombre de if imbriqués dans une fonction (cyclomatic-complexity) permet de rendre le code plus maintenable.
  • Déclarer des variables uniquement si on les utilise (no-unused-variable) permet d’assurer la bonne fonctionnalité du programme.

Alors pour faire simple, dans cet article nous parlerons d’erreurs de formatage en référence aux règles de lisibilité et d’erreurs fonctionnelles en référence aux règles de maintenabilité ou de fonctionnalité.

Et voilà où je voulais en venir : si TSLint est capable d’identifier les erreurs de formatage alors pourquoi déléguer cette tâche à Prettier ?

La réponse en est que TSLint ne permet pas d’automatiser totalement le formatage de code. Par exemple, la règle max-line-length ne peut pas être corrigée automatiquement (on ne peut pas exécuter tslint --fix en ligne de commande pour cette règle) alors que comme nous le verrons, Prettier prend très bien cela en charge. De plus TSLint ne formate que les fichiers TypeScript alors que Prettier formate d’autres types de fichiers comme les fichiers HTML et CSS par exemple.

C’est pourquoi nous allons garder TSLint pour ce qu’il fait de mieux, à savoir la recherche des erreurs fonctionnelles et prendre Prettier pour l’automatisation du formatage de code.

Aller, cette fois c’est parti !

Configurer Prettier

Ajoutons un fichier .prettierrc à la racine de l’espace de travail :

{
 "singleQuote": true, 
"printWidth": 140, 
"trailingComma": "all" 
}

Ce fichier de configuration va surcharger la configuration par défaut de Prettier. Il nous suffit donc d’ajouter les propriétés spécifiques qui correspondent à notre besoin.

Utilisons les guillemets simples avec la propriété singleQuote et augmentons la longueur des lignes à 140 caractères, pour coller au plus proche de la configuration TSLint d’origine. Cependant, rien ne vous empêche de changer ces règles pour les adapter à votre style. Enfin, à titre personnel, j’aime bien utiliser la propriété trailingComma pour ajouter une virgule au dernier élément d’un tableau multi-lignes par exemple. Mais là aussi, rien ne vous empêche de faire autrement.

Ensuite, ajoutons un autre fichier .prettierignore pour lister les fichiers que nous ne souhaitons pas formater.

/* 
!/e2e 
!/projects 
!/src 
assets/ 
browserslist 
package*.json 
*.ico

Cette configuration se base sur la syntaxe des fichiers .gitignore. Nous indiquons ici qu’il faut ignorer tous les fichiers de l’espace de travail à l’exception de /e2e, /projects et /src qui contiennent notre code métier. Pour rappel, vous aurez un dossier /projects si vous générez une librairie avec la commande ng generate library). Enfin, à l’intérieur des dossiers restants, on ignore certains dossiers (comme assets/) et certains fichiers (comme package.json et package-lock.json, dont le formatage est géré automatiquement par NPM).

Configurer TSLint pour qu’il n’interfère pas avec Prettier

Prettier est maintenant bien configuré, mais TSLint marche encore sur ses plates-bandes. Désactivons les règles de formatage de code propres à TSLint, afin de laisser Prettier libre pour faire ce travail. Pour cela, nous allons installer le paquet tslint-config-prettier dont le nom est assez explicite :

$ npm i -D tslint-config-prettier

Le contenu de ce paquet est très simple et voici à quoi ressemble la configuration qu’il fournit :

{
  "rules": {
    "align": false,
    "array-bracket-spacing": false,
    "arrow-parens": false,
    ...
  }
}

Vous voyez, il met simplement à false toutes les règles de formatage de code gérées par TSLint.

Pour l’utiliser, ajoutons-le au fichier tslint.json à la racine de notre espace de travail, qui permet justement de configurer TSLint :

{
  "extends": [
    "tslint:recommended",
    "tslint-config-prettier"
  ],
  "rules": {
    ...
  },
  "rulesDirectory": [
    "codelyzer"
  ]
}

Attention, placez bien la configuration tslint-config-prettier en dernière position du tableau extends !

Nous y sommes presque ! La section rules de ce même fichier tslint.json a priorité sur ce qui est défini dans la section extends. Nous devons donc retirer de cette section les règles de formatage de code, sans toucher aux règles d’analyse statique d’erreurs fonctionnelles spécifiques d’Angular.

Pour identifier ces règles, nous avons 2 solutions :

Bon aller, je vous donne la solution ! Voici les 5 règles que nous devons retirer :

  • arrow-parens
  • max-line-length
  • no-consecutive-blank-lines
  • quotemark
  • trailing-comma

Finalement notre fichier tslint.json devrait maintenant ressembler à cela (j’utilise la version 8.3 d’Angular) :

{
  "extends": [
    "tslint:recommended",
    "tslint-config-prettier"
  ],
  "rules": {
    "array-type": false,
    "deprecation": {
      "severity": "warning"
    },
    "component-class-suffix": true,
    "contextual-lifecycle": true,
    "directive-class-suffix": true,
    "directive-selector": [
      true,
      "attribute",
      "app",
      "camelCase"
    ],
    "component-selector": [
      true,
      "element",
      "app",
      "kebab-case"
    ],
    "import-blacklist": [
      true,
      "rxjs/Rx"
    ],
    "interface-name": false,
    "max-classes-per-file": false,
    "member-access": false,
    "member-ordering": [
      true,
      {
        "order": [
          "static-field",
          "instance-field",
          "static-method",
          "instance-method"
        ]
      }
    ],
    "no-console": [
      true,
      "debug",
      "info",
      "time",
      "timeEnd",
      "trace"
    ],
    "no-empty": false,
    "no-inferrable-types": [
      true,
      "ignore-params"
    ],
    "no-non-null-assertion": true,
    "no-redundant-jsdoc": true,
    "no-switch-case-fall-through": true,
    "no-use-before-declare": true,
    "no-var-requires": false,
    "object-literal-key-quotes": [
      true,
      "as-needed"
    ],
    "object-literal-sort-keys": false,
    "ordered-imports": false,
    "no-conflicting-lifecycle": true,
    "no-host-metadata-property": true,
    "no-input-rename": true,
    "no-inputs-metadata-property": true,
    "no-output-native": true,
    "no-output-on-prefix": true,
    "no-output-rename": true,
    "no-outputs-metadata-property": true,
    "template-banana-in-box": true,
    "template-no-negated-async": true,
    "use-lifecycle-interface": true,
    "use-pipe-transform-interface": true
  },
  "rulesDirectory": [
    "codelyzer"
  ]
}

Et voilà, Prettier et TSLint sont bien configurés dans notre application Angular !

Exécuter Prettier sur l’espace de travail

Pour exécuter Prettier, il nous faut maintenant l’installer :

$ npm i -D prettier

L’exécutable est accessible depuis le dossier ./node_modules/.bin/prettier. Nous pouvons exécuter la commande avec l’option --check (pour lister les fichiers problématiques sans les modifier) ou avec l’option --write (pour les modifier). C’est magique !

$ ./node_modules/.bin/prettier --check "**"
 
Checking formatting...
e2e/protractor.conf.js
e2e/src/app.e2e-spec.ts
e2e/tsconfig.json
src/app/app.component.html
src/app/app.component.spec.ts
src/app/app.component.ts
src/app/app.module.ts
src/environments/environment.prod.ts
src/environments/environment.ts
src/index.html
src/main.ts
src/polyfills.ts
src/test.ts
Code style issues found in the above file(s).
 
$ ./node_modules/.bin/prettier --write "**"

Si besoin, nous pouvons ajouter un script NPM au fichier package.json, pour nous simplifier la vie :

{
  ...
  "scripts": {
    ...
    "prettier:check": "prettier --check \"**\"",
    "prettier": "prettier --write \"**\"",
    ...
  },
  ...
}

Intégrer Prettier à notre IDE

Si comme moi vous utilisez VSCode, je vous propose alors d’installer le plugin Prettier – Code formatter. Celui-ci va nous permettre de formater un fichier ouvert dans notre IDE à partir du menu contextuel « Formatter le document », sans passer par la ligne de commande.

Nous pouvez même aller plus loin en configurant VSCode pour formater un fichier ouvert dès que nous le sauvegardons, avec l’option editor.formatOnSave :

{
  "editor.formatOnSave": true
}

Si vous êtes plutôt IntelliJ, voici le plugin équivalent pour votre IDE : Prettier.

Exécuter Prettier avant chaque commit avec husky et pretty-quick !

Nous sommes maintenant capables d’exécuter Prettier en ligne de commande ou directement depuis notre IDE. Si comme moi vous utilisez Git comme gestionnaire de source, je vous propose alors d’ajouter un « hook » pour exécuter Prettier avant chaque commit, sur tous les fichiers modifiés. Pour rappel, les crochets (« hooks ») sont tout simplement des scripts qui s’exécutent automatiquement lorsqu’une action bien précise est déclenchée.

Installons 2 autres paquets qui vont nous permettre très simplement d’exécuter Prettier avant chaque commit :

$ npm i -D husky pretty-quick
  • husky : permet de configurer les hooks de Git.
  • pretty-quick : permet d’exécuter Prettier uniquement sur les fichiers qui ont été modifiés.

Il ne reste plus qu’a configurer husky pour exécuter pretty-quick avant chaque commit. Nous pouvons par exemple renseigner cette configuration dans le fichier package.json :

{
  "scripts": {
    ...
  },
  ...
  "husky": {
    "hooks": {
      "pre-commit": "pretty-quick --staged"
    }
  }
}

Et voilà, le tour est joué !

Take away

Dans ce tutoriel, vous avez appris à configurer Prettier dans vos applications Angular, adapter la configuration TSLint en conséquence et utiliser Prettier pour formater votre code en ligne de commande, dans votre IDE ou automatiquement avant chaque commit Git.

Et rappelez-vous que je vous ai économisé l’écriture d’un long guide de contribution à l’attention de votre équipe, alors happy coding ^^ !

Ressource

Code source du tutoriel sur notre dépôt Github : https://github.com/xebia-france/angular-prettier.

Publié par

Publié par Stéphane Francel

Consultant Senior JavaScript

Commentaire

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Nous recrutons

Être un Xebian, c'est faire partie d'un groupe de passionnés ; C'est l'opportunité de travailler et de partager avec des pairs parmi les plus talentueux.