This is not 'Nam.

This is my blog. There are rules.

ESLint et Prettier

Publié le 04 mars 2018

C'est quoi un linter ?

Lint (logiciel) lint (en lettres minuscules ; de l'anglais lint : « touffe hirsute » ) est une commande UNIX de préprocesseur permettant l'analyse statique de code source en langage C. Wikipedia

Un linter est donc un outil qui va permettre d'analyser du code et vérifier s’il respecte un certain nombre de règles de syntaxe et de qualité.

En JavaScript, le linter le plus connu est certainement Eslint, dont l'un des points fort est sa configuration. Il est tellement configurable que j'ai bien souvent copié/collé la conf. d'un projet précédent sur le suivant, pour ensuite perdre du temps à installer des plug-ins manquants, rajouter des règles afin d'activer ou désactiver des alertes intempestives...

Pour se faciliter la vie, il vaut donc mieux reprendre les choses à zero, bien comprendre le fonctionnement, et n'installer que le strict minimum, quitte à devoir rajouter des règles et autre plugins au fur et à mesure.

Installation et utilisation

Eslint peut être installé en global ou au niveau d'un projet. Personnellement, et parce que chaque projet va requérir ses propres règles et souvent ses propres plugins, je préfère l'installer au niveau du projet, voir de chaque partie du projet.

yarn (workspace workspacename) add -D eslint

Une fois installé, on a accès à l'utilitaire depuis la ligne de commande :

❯ node_modules/.bin/eslint index.js

/home/alexis/Code/Sandbox/javascript-playground/services/express_server/index.js
  6:3  error  Insert `··`  prettier/prettier

✖ 1 problem (1 error, 0 warnings)
  1 error, 0 warnings potentially fixable with the `--fix` option.

On voit donc les erreurs éventuelles présentes dans index.js, ce qui est bien, mais certainement pas suffisant.

On pourrait décider de lancer Eslint à chaque fois que l'on ajoute du code au repository (Pre-commit Hook). Cela peut sûrement être utile, mais je n'aime pas l'idée d'avoir du code ajouter au repo avec un formatage que je n'aurais pas vu au préalable. Je préfère laisser mon editeur faire appel à ESLint pour corriger mon code en live.

Et si vous êtes sous VSCode, c'est très simple avec le plugin VS Code ESLint extension. Si vous utilisez un autre éditeur, il y a de forte chance que ESLint puisse aussi y être intégré.

Et toujours pour une affaire de goût, j'aime activer le formatage automatique du code à la sauvegarde.

// in $HOME/.config/Code/User/settings.json
"eslint.autoFixOnSave": true,

Configuration de base

Le linter est installé et l'IDE configuré pour l'utiliser. Il est maintenant temps de le configurer, via un fichier .eslintrc

// in .eslintrc
{
    "parser": "Espree", // Le parser utiliser pour analyser le code
    "parserOptions": {}, // Les options éventuelles passées au parser
    "env": {}, // Déclaration d'un environnement permettant de définir des variables globales préconfigurées
    "globals": {}, // Déclaration de variables globales personnalisées
    "plugins": [], // Déclaration et configuration des plugins
    "extends": [], // Déclaration d'une pré-configuration complête d'ESLint
    "rules": {}, // Surcharge des règles appliquées par défaut par ESLint ou par un plugin
}

Sur un projet basique proche du standard Javascript supporté par le node ou le navigateur utilisé (donc pas de Babel), la configuration par défault est presque suffisante :

{
    "env": {
        "es6": true,
        "node": true,
    }
}

Linted !

Mais l'application de le configuration recommandée par ESLint apporte quand même du mieux :

{
    "env": {
        "es6": true,
        "node": true,
    },
    "extends": [
        "eslint:recommended",
    ]
}

Much better Linted !

Une application CRA (create-react-app)

Pour pouvoir utiliser ESLint depuis VSCode sur un application CRA, il va falloir déclarer son propre fichier de configuration, sauf à vouloir passer par un create-react-app eject pour acceder à la configuration mise en place par l'équipe du projet. Et ce type de projet est beaucoup plus compliqué que celui évoqué précédemment : passage par un phase de transpilation avec Babel (il faut donc changer de parser), présence de jsx et son lot de rules à définir ...

Mais, et merci à eux, l'équipe de Facebook met à disposition une configuration déjà toute prête : eslint-config-react-app. On doit certe installer pas mal de chose ...

yarn add -D eslint-config-react-app babel-eslint@^7.2.3 eslint@^4.1.1 eslint-plugin-flowtype@^2.34.1 eslint-plugin-import@^2.6.0 eslint-plugin-jsx-a11y@^5.1.1 eslint-plugin-react@^7.1.0

...mais cela fait, le configuration reste très simple grâce à extends :

// in .eslintrc
{
    "env": {
        "es6": true,
        "jest": true
    },
    "extends": ["react-app"],
}

On applique maintenant les mêmes règles de syntaxe dans VSCode que celles définies par CRA.

Voyons par exemple le fichier App.js avec notre configuration précédente:

CRA basic config !

Maintenant avec

CRA with good config !

Les import sont valides, et ceci parce que l'on n'utilise plus le parser Espree mais babel-eslint ! Comment on peut le savoir ? En regardant dans le code du plugin où l'on va retrouver la configuration complète d'ESLint. Et c'est du serieux:

// in node_modules/eslint-config-react-app/index.js
module.exports = {
  root: true,

  parser: 'babel-eslint',

  plugins: ['import', 'flowtype', 'jsx-a11y', 'react'],

  env: {
    browser: true,
    commonjs: true,
    es6: true,
    jest: true,
    node: true,
  },

  parserOptions: {
    ecmaVersion: 6,
    sourceType: 'module',
    ecmaFeatures: {
      jsx: true,
      generators: true,
      experimentalObjectRestSpread: true,
    },
  },

  rules: {
    // http://eslint.org/docs/rules/
    'array-callback-return': 'warn',
    'default-case': ['warn', { commentPattern: '^no default$' }],

    ...

    'jsx-a11y/role-supports-aria-props': 'warn',
    'jsx-a11y/scope': 'warn',
    // https://github.com/gajus/eslint-plugin-flowtype
    'flowtype/define-flow-type': 'warn',
    'flowtype/require-valid-file-annotation': 'warn',
    'flowtype/use-flow-type': 'warn',
  },
};

Prettier

ESLint fait maintenant un super boulot, mais pèche un peu sur le formatage du code. Ce doit être améliorable en définissant des rules spécifique, mais il existe un autre projet Prettier justement spécialisé dans le formatage du code. Il est complètement indépendant d'ESLint et utilise son propre parser et ses propres règles.
Il va donc falloir ajouter une deuxième configuration et appliquer un second outil sur le code ? Et ne risquent-ils pas de se marcher sur le pied ?
Et bien si, il pourrait effectivement se concurrencer. Mais comme le monde du JavaScript est un monde merveilleux ESLint et Prettier peuvent fonctionner l'un avec l'autre, ESLint étant capable de donner le relai à Prettier pour tout ce qui concerne le formatage du code, et uniquement cela. Encore une fois, on passe par une configuration extends :

yarn add -D prettier eslint-plugin-prettier eslint-config-prettier

Et voici la configuration après quelques ajouts de règles pour avoir un formattage de code correspondant à mes habitudes :

// in .eslintrc
{
    "env": {
        "es6": true,
        "node": true
    },
    "extends": [
        "eslint:recommended",
        "plugin:prettier/recommended"
    ],
    "rules": {
        "prettier/prettier": ["error", {
            "singleQuote": true,
            "tabWidth": 4,
            "trailingComma": "all"
        }]
    }
}

Encore plus ?

La configuration précédente est déjà très efficace ! Et bien qu'il faille sans doute être raisonnable sur l'ajout de plugins, je complèterais quand même ce set de configuration minimal d'ESLint par un dernier plugin, eslint-plugin-jest :

{
    "env": {
        "es6": true,
        "node": true,
        "jest": true
    },
    "extends": [
        "eslint:recommended",
        "plugin:prettier/recommended",
        "plugin:jest/recommended"
    ],
    "rules": {
        "prettier/prettier": ["error", {
            "singleQuote": true,
            "tabWidth": 4,
            "trailingComma": "all"
        }],
        "jest/consistent-test-it": [
            "error",
            {"fn": "it"}
        ]
    }
}

Conclusion

Les possibilités de configuration d'ESLint sont conséquentes et il n'est pas toujours facile de trouver les bonnes. A mon sens, et surtout en ce qui concerne les règles de formatages du code, les bonnes pratiques se définissent au sein de la communauté. C'est pour cela que j'aime bien me reposer sur les extends de la configuration pour garder cette configuration la plus simple possible. Quitte à devoir me plier à des règles de qualité et de syntaxe qui ne seraient initialement pas les miennes.

Sans compter qu'il est ensuite très facile d'étendre ou de désactiver ces règles en fonction du projet.

Veille sur le sujet