Setup d’un projet Webpack avec Babel pour ES2017, VueJS, Karma, Mocha, Chai et toute la clique

Note : Malgré tous mes efforts, je n’ai pas réussi à faire fonctionner Karma, Webpack et PhantomJS correctement ensemble. L’ensemble des problèmes rencontrés est résumé en anglais ici. Concrètement, nous rencontrons des erreurs lors de l’exécution de Karma qui ne sont pas les mêmes d’un environnement à un autre et qui semblent tous liés à un problème de transpilage côté Webpack. Il est donc très probable qu’on abandonne Karma, Webpack et PhantomJS très rapidement pour d’autres choses.

Ça fait longtemps que j’ai pas écrit un article technique. Et comme il se trouve qu’il n’y a pas si longtemps, j’en ai un peu chié pour faire la config correcte d’un environnement JS pour un projet Django, je me disais que c’était une bonne occasion de s’y remettre.

Soyez prévenus, tout de même, que je ne supporte pas Node, NPM et tout son écosystème. Il risque donc d’y avoir dans cet article une pointe de mauvaise foi.

Introduction dans tes orifices

Merde… Je me mets à écrire des articles à la Sam&Max. Je vais mal finir…

Il y a très peu de temps, j’ai commencé à contribuer à SocialHome, le projet de réseau social décentralisé de Jason Robinson, lui aussi ancien contributeur pour diaspora*. Dans la vie, Jason est un mec bien. Mais il est aussi développeur Python/Django. C’est donc tout logiquement que SocialHome est développé en Python/Django. Mais SocialHome n’en est encore qu’à ses tous premiers stade de développement. Jason se concentre pour le moment sur le développement de back-end et de federation, une bibliothèque de support des protocoles de réseaux sociaux décentralisés 1. Le front-end de SocialHome est donc pour le moment… comment dire… Lacunaire. Ouais, bon, Ok. Il est dégueulasse. C’est de l’insertion de HTML en jQuery, ça repose Grunt et Bower que plus personne n’utilise… Bref, ça a besoin d’un bon coup de ménage.

Fort heureusement pour moi, le front-end n’est pas très avancé. Le projet n’est pas bloqué avec des technos obselètes comme peut l’être diaspora* et son core-développeur est très réactif dans les revues de code et très présent sur IRC 2. Et il se trouve que le projet avait besoin d’un développeur front. Comme il se trouve que j’ai passé mes trois dernières années à développer le l’interface utilisateur de diaspora* et que je sortais d’une mission de développeur ReactJS, je me suis dévoué pour prendre ce rôle. Et, cela étant dit, vu à quel point l’écosystème Javascript et NodeJS me révulse, je dois être un poil maso sur les bords.

Étape 1 : Webpack, Babel et VueJS

Bon, j’ai pu tester ReactJS/JSX lors de ma dernière mission pro. Et je trouve que c’est plutôt de la bonne came. Mais, pour ce projet, j’avais envie de tester VueJS, que le copain Marien Fressinaud utilise pour l’un de ses projets et dont j’ai entendu de bons échos. Mais comme chuis un mec un peu bourrin, j’ai pas voulu commencer par la méthode douce « j’inclue la biblio VueJS et je fais un peu de templating dans mon objet Javascript ». Trop facile. Nan, moi j’ai préféré sortir l’artillerie lourde dès le début : Webpack et les single-file components 3. À ma décharge, SocialHome a besoin de moderniser son écosystème de composants JS. Le Gruntfile, c’est mignon, mais on a fait mieux, depuis.

J’en profite d’ailleurs émettre ici une petite note sur l’écosystème Javascript : il pue la merde 4. Alors, j’ai suffisamment gueulé dans le passé sur Java et sa communauté. Et c’est vrai que le langage est syntaxiquement tout pérave. Mais il possède l’un des écosystèmes les plus solides et matures que j’ai pu rencontrer. Maven a imposé un ensemble de conventions fortes qui n’ont pas bougé avec le temps et sont restés. Même si vous choisissez un des moteurs de compilation qui sont arrivés après comme Gradle ou SBT. Toute la configuration du projet se trouve dans votre pom.xml. Que ce soit ses dépendances, sa chaîne de compilation ou la configuration de ses plugins. Tout est dans le même fichier. Et c’est pareil pour Gradle ou SBT.

Par rapport à Maven, l’écosystème NodeJS donne l’impression d’être revenu 10 ou 15 ans en arrière. NPM et son package.json  ne font que gérer les dépendances… à l’exception de commandes shell que vous pouvez déclarer et exécuter avec npm run <commande>. Mais c’est pas plus avancé qu’un Makefile. Alors pour des projets plus gros, il vous faudra un autre moteur de compilation. Et il en existe plus d’une dizaine : Grunt, Gulp, Mimosa, Brocolli ou encore Webpack pour les plus connus. De toutes façons, tous les 6 mois il y en a un qui est créé et un qui tombe en désuétude. Donc si vous créez un setup JS, gardez en tête que, de toutes façons, vos choix ne sont pas pérennes. Et, bordel, même pour la gestion de dépendances, c’est la merde. Certes, NPM est un standard. Mais apparemment, c’est suffisamment pété pour qu’en plus de ça, certains projets rajoutent un deuxième moteur de gestion de dépendances. Bower a eu un gros succès il y a un an ou deux, maintenant, Yarn — alors… pas le Yarn de Hadoop, on est d’accord, hein ? — tend à s’imposer sur les nouveaux projets.

Et puis, puisqu’un seul fichier pour configurer tout votre projet, c’est beaucoup trop simple, chaque nouvel outil a sa propre manière de déclarer sa configuration. Il n’est encore venu à l’idée de personne qu’on pouvait modifier NPM pour intégrer un système de plugins configurables dans le package.json comme on peut le faire avec Maven…

Bref…

Étape 1 : Webpack, Babel et VueJS (bis repetita)

Les dépendances NPM

Donc, moi j’ai décidé de faire de l’ES2017. Parce que l’écosystème JS est suffisamment aux fraises pour devoir en plus se taper tous les trous du langage qui ont commencé à être fixés avec ES2015. Et puis j’ai décidé d’utiliser Webpack pour coordonner tout ça parce que plouf, plouf. De toutes façons, dans 2 ans une autre ConnerieJS l’aura remplacé. Commençons par inclure toutes nos dépendances dans le package.json :

Petite note sur babel-preset-env : au tout début, j’ai inclu les dépendances babel-core, babel-loader et babel-preset-es2017. Et puis je me suis rendu compte, en ajoutant un test unitaire qu’ils fonctionnait avec Firefox mais pas avec PhantomJS. Très étrangement, PhantomJS se vautrait sur la syntaxe lambda. Tout se passait comme si la syntaxe ES2015 n’était pas transpilée par Babel. Et, en fait c’est exactement ce qui se passait… Naïvement, j’ai pensé que  babel-preset-es2017 incluait toutes les spécifications ES depuis ES2015. Ce n’est pas le cas.  babel-preset-es2017 ne transpile que les fonctionnalités nouvelles dans la spécification 2017 d’ES. Les lambdas font partie de la spécification 2015, que Firefox supporte, mais que PhantomJS ne supporte pas.

Conclusion : vous faites pas chier. Adoptez babel-preset-env. Ça contient toutes les vitamines et les minéraux dont votre ES a besoin pour transpiler.

Chez nous, a SocialHome, on a aussi choisi SCSS, pour notre CSS. node-sass, sass-loader  et style-loader nous permettent de transpiler notre SCSS en CSS bien gras avec des grumeaux. Enfin, vue-loader va nous permettre de transformer nos single-file components en JS et en CSS compréhensibles par le navigateur.

La config Webpack

Notre webpack.config.js. Attention… C’est parti :

Yolo !

Petite note sur la partie :

La plupart des exemples de config Webpack que vous trouverez sur internet crachent tout votre JS dans un seul gros bundle. Pas très utile si vous avez une grosse application. Faire télécharger plein de code JS inutile parce qu’il ne sert que dans une autre page, c’est pas cool. Webpack vous permet de mettre un dictionnaire à la place d’une chaîne pour le champ entry. Ce qui permet de déclarer plusieurs fichiers d’entrée. La clef utilisée dans ce dictionnaire peut-être réutilisé pour générer plusieurs fichiers de sortie dans le champ output : filename: "webpack.[name].js"[name] correspond à la clef dans le champ entry. Ici, notre config Webpack va générer un fichier socialhome/static/js/webpack.stream.js. Trop métal ! \m/ >.< \m/

À propos des loaders, et dans l’ordre :

  1. tous nos fichiers JS sont parsés et transpilés avec Babel ; rien d’extraordinaire ici,
  2. tous nos fichiers SCSS sont parsés avec SASS mais ne me demandez pas pourquoi il faut css-loader  aussi ; j’en sais rien,
  3. nos single-file components sont parsés avec vue-loader.

vue-loader accepte une option loaders qui permet de parser les fichiers les SFC avec différents loaders. Dans notre cas, le JS est parsé avec Babel pour pouvoir utiliser ES2017 dans nos SFC et le CSS avec SASS. Sachez que vous pouvez utiliser tout plein d’autres loaders. Vous pouvez utiliser Pug plutôt que Vue pour votre template ou Less plutôt que SASS pour votre style.

Pour la partie resolve, on ajoute un alias: {vue: "vue/dist/vue.js",} pour pouvoir faire import Vue from "vue" dans les SFC plutôt que import Vue from "vue/dist/vue" parce qu’on a la flemme.

Et, enfin, devtool: "cheap-module-source-map" nous permet d’avoir du source-mapping dans notre navigateur. Demandez pas comment ça marche, j’en sais rien. Farcissez-vous la doc si ça vous fait marrer. Moi, je trouve que j’ai déjà passé beaucoup trop de temps à configurer.

Dernière chose, pensez bien à rajouter une cible dev dans votre package.json pour transpiler tout ce fatras à la volée à chaque changement dans votre code. Sinon, ça va vous rendre dingue pendant le développement :

Normalement, avec ce setup, vous devriez pouvoir créer des fichiers .vue à la cool. Si c’est pas le cas, démerdez-vous. Je fais pas le service après-vente de l’écosystème NodeJS, non plus !

Étape 2 : PhantomJS, Karma, Mocha et Chai

Comme je vous l’ai déjà dit, l’écosystème NodeJS, c’est toupété. Parce que si vous vous imaginez qu’écrire des tests unitaires pour votre projet, ça devrait être simple comme ajouter une dépendance JUnit à votre pom.xml, les développeurs JS, eux, ils pensent pas comme vous, et ils vous emmerdent ! C’est donc, non pas 2, ni 3 outils que nous devons utiliser pour tester notre projet, mais 4. Parce que tagueule. Me demandez pas pourquoi Jason a insisté pour utiliser Karma + Mocha + Chai. Si j’avais su que c’était aussi naze, je l’aurais engueulé avant de lui dire d’aller se faire foutre et que, moi, j’utilisais Jasmine épicétou. Mais chuis un mec sympa, ché pas pourquoi…

Alors, PhantomJS, Karma, Mocha et toutes ces conneries new age avec des noms qui viennent d’orient pour faire mouiller les hipsters, cékoi ?

PhantomJS, c’est un navigateur en JS conçu pour faire tourner des TU sur votre font-end. Parce exemple pour tester que votre jQuery, il insère bien votre soupe HTML là où qu’il faut. Karma, c’est un moteur pour faire tourner vos tests. Mocha, c’est une biblio pour écrire vos tests et Chai, c’est une biblio pour faire les assertions de vos tests.

Vous voyez, la volonté de pas faire de choix et de pas avoir d’opinion tranchée, c’est aussi une autre plaie de l’écosystème Javascript. C’est comme ça qu’on se retrouve à avoir 76 outils pour faire la même chose, mais juste un peu différemment du voisin, plus 616 outils pour faire une seule chose un peu compliquée, mais qu’on veut laisser le choix au développeur de la couleur du cuir du fouet avec lequel il va devoir se punir lui-même. Et c’est comme ça qu’on empêche toute forme de convention d’émerger… À ce rythme-là, l’écosystème NodeJS sera mature dans à peu près 53 ans…

Ah, oui ! Je vous ai dit que Chai vous propose aussi 3 putains de syntaxes différentes pour faire vos assertions de tests !? Si, si

Les dépendances

Bon, allez, commençons par les dépendances :

Et là, il faut que je commence par un petit mot : si vous voulez utiliser la syntaxe should de Chai avec l’initialisation import 'chai/should'; décrite dans la doc, faites bien attention à utiliser Chai 4. Ah oui, et puis la doc n’est pas correcte : il faut écrire import 'chai/register-should';. Parce nique les docs à jour, ok !?

Configuration de Karma

Pour configurer Karma, il faut créer un fichier karma.conf.js . Ouais, nan, pas karma.config.js , comme pour Webpack, nan, c’est trop simple. C’est bien karma.conf.js. Je vous ai dit que les conventions, c’était pas top le fort de l’écosystème NodeJS ?

Et donc, pour la config de Karma, ça se passe par là :

Nous, comme on a choisi de foutre de l’ES2017 partout, dans nos fichiers .vue comme dans nos tests, il faut que Karma passe par Webpack avant de lancer les tests. Donc on commence par récupérer la config :

Je sais pas pourquoi wpConf.devtool = "inline-source-map". J’imagine que c’est une option de source-mapping plus performante à la compilation que cheap-module-source-map.

Pour l’option files  de la config, on peut spécifier un glob (voir la section File patterns). Pas besoin de lister les tests un par un comme je me l’était figuré au début, donc. Bien préciser dans l’option  preprocessors qu’on veut faire du sourcemap et du webpack (j’ai l’air intelligent à paraphraser ce qu’il y a écrit dans la conf, là, ou bien !?).

Enfin, ne pas oublier de donner la conf de Webpack qu’on a importée en début de conf : webpack: wpConf.

Normalement, avec ça, on devrait pourvoir faire des tests à la cool :

Ouais, ben c’était pour tester ma configuration de test ! Me jugez pas, hein !

Enfin, on rajouter une cible de test dans notre package.json :

Conclusionnage en mode relou

J’ai pas encore testé ma conf Karma sur des vrais tests de composants Vue. Mais j’ai confiance. Ça devrait bien se passer. Comme pour tout en JS ! Si le code qui est à l’origine de cet article vous intéresse, il est . Bien évidemment, si j’ai des ajouts ou des corrections à faire à cet article, elles viendront avec le temps.

Sur ce, la bise à tous et à toutes ! <3

Edit 1 : .babelrc  FTW

Donc là, j’ai écrit des vrais tests pour mon code tout potelé et ça a merdé. Une dépendance à la con de VueJS qui utilise le mot-clef const qu’évidemment, PhantomJS ne comprends pas, parce que PhantomJS est resté bloqué en 2004 >.<

Normalement, Webpack aurait dû faire son taff ici et dire à Babel de tout transpiler en mode tagueule mais, visiblement, Webpack a une tare et Babel s’est pas configuré correctement. Solution crado : on rajoute un gros .babelrc  à la racide avec exactement ce qu’il y a écrit dans la conf Webpack, parce que j’adore me répéter : {presets: ["env"]}. Comme si j’avais pas déjà assez de .machin_de_config  à la racine…

Notes de bas de page :

  1. federation ne supporte pour le moment que le protocole diaspora* mais prévoit à terme d’en supporter d’autres, donc la spécification ActivityPub du W3C
  2. Je vous ai dit que c’est un mec bien !
  3. Je vous avais dit que j’étais maso.
  4. Ah, mais j’avais prévenu pour la mauvaise foi, moi ! Si vous êtes pas contents, c’est votre problème !

Déjà 4 avis pertinents dans Setup d’un projet Webpack avec Babel pour ES2017, VueJS, Karma, Mocha, Chai et toute la clique

Tu as besoin du loader css après scss car scss te génère du css.

Avec du css: d’abord le loader css pour que webpack comprenne qu’il faut transformer du css, puis file-loader pour mettre ce fichier (css, mais on s’en fout-c’est un fichier) dans le bundle.

Avec du scss: d’abord le loader scss pour générer du css, après tu retombes sur du css, donc voir plus haut.

Je suis loin d’être un expert en webpack mais il semblerait qu’il y ait toujours une philosophie de piping, d’un format à un autre, jusqu’au fichier.

Pour l’instant, je n’ai pas de problème de CSS. Le projet utilise encore Grunt pour générer son CSS mais ça fait partie des choses qu’il faut refactoriser. J’ai écrit mon Webpack un peu à l’arrache et j’en ai conscience. J’ai encore un énorme problème avec le setup PhantomJS/Karma/Webpack. Impossible de le faire fonctionner correctement…
Il est donc fort possible que je réécrive un article pour décrire notre nouvelle config parce qu’on va très probablement jeter PhantomJS et Karma.

Quand on aura réussi tout ça, on passera au CSS pour dégager Grunt. Ce truc est une horreur. Je continuerai de faire des édits à l’article au fur et à mesure. Si je recontre un problème avec le CSS, je modifierai l’article en conséquence. Merci pour ta remarque.

Laisser un commentaire

indique des champs obligatoire.