Accueil Nos publications Blog Yeoman : que tous ceux qui sont dans la vibe disent « Yo »

Yeoman : que tous ceux qui sont dans la vibe disent “Yo”

yeoman-main-logoLa Tendance à la standardisation des développements issue du back-end se diffuse de plus en plus dans l’univers front-end. De nouveaux outils font leur apparition pour accompagner cette mutation. C’est de l’un d’eux, Yeoman, que je souhaite vous entretenir aujourd’hui. Yeoman, c’est un peu l’assistant qui va accompagner le développeur front-end dès le début du processus de développement.

Qu’est ce que Yeoman

Le projet Yeoman

Yeoman est un projet open-source distribué sous licence BSD et soutenu par Google. D’ailleurs le projet compte plusieurs contributeurs employés par la firme. Yeoman est bâtit autour de trois projets indépendants : Grunt, Bower et Yo. Ces trois projets sont en réalité des modules de NodeJs. Nous verrons dans cet article que Yeoman n’est pas seulement une simple addition de ces trois outils mais qu’il va plus loin en offrant une intégration poussée et surtout un workflow.

Node.js

Avant d’aller plus loin, une précision quant à NodejJs qui sera plusieurs fois évoqué dans cet article. Node.js est un framework multifonctions qui fournit un environnement d’exécution du Javascript hors navigateur.

Toutes les fois qu’on souhaitera développer en Javascript un outil destiné à tourner hors du navigateur, on va s’orienter naturellement vers Node.js. On développera donc notre projet afin qu’il puisse tourner sous Node.js : ce faisant nous développons ce qu’on appelle “un module NodeJs”. Par la suite, je parlerai souvent de “module node” et de “npm“. En effet, les outils que nous verrons dans cet article sont destinés à être utilisé pendant le développement et non en production. Ils s’exécutent tous hors du navigateur et sont tous des modules Node.js. NPM (Node Package Manager) est un gestionnaire de paquet qui va nous permettre de gérer nos modules Node.js. Utiliser Node.js signifie donc d’avoir au préalable installé Node.js lui-même et NPM.

Node.js mériterait toute une série d’articles à lui seul, aussi si vous n’êtes pas familier avec ce framework, je vous conseille cet excellent tutoriel.

Au demeurant, vous n’avez nullement besoin d’en savoir davantage pour poursuivre la lecture de ce post.

Périmètre fonctionnel

Yeoman, c’est l’addition de trois fonctionnalités :

  • Génération du squelette de l’application
  • Gestion des dépendances
  • Automatisation des tâches

Génération du squelette de l’application

L’objectif est de créer pour un type de projet donné, le squelette de projet avec la bonne arborescence de fichier et de répertoire. Sans de tels outils, il est possible de développer mais pas de façon optimale :

  • création des nouveaux projets à partir de projets existants
  • création des nouveaux projets à partir d’un model personnel conservé quelque part sur un repository privé (file system, CVS, etc.)
  • création “from scratch”, au besoin inventer un nouveau model parce que finalement, mettre les images dans le répertoire “my_pictures”, c’est mieux que le répertoire “images” !!!

On ne peut pas se satisfaire de ces solutions car, tôt ou tard nous finissions tous par maintenir le projet de quelqu’un d’autre. Et il est donc dans notre intérêt commun d’avoir une structure standardisée pour chaque type de projet.

Par ailleurs, pour les débutants, c’est souvent difficile de trouver par soi-même une structure correcte. Avoir un outil qui vous génère un projet vide, permet d’être certain de partir sur une bonne base.

Dans yeoman, la génération des squelettes d’application est du ressort des générateurs. Je reviendrai sur les générateurs Yeoman un peu plus bas.

Gestion des dépendances

Une webapp aujourd’hui utilise forcément des composants externes. Par composant, je veux parler de toutes les ressources que nous ne créons pas nous-même mais qui sont nécessaires au fonctionnement de l’application. Il ne s’agit donc pas des outils de développement tel Yeoman mais des librairies Javascript, des fichiers CSS et autres utilisés dans le code.

Historiquement, les dépendances sont gérées à la main. Le développeur va sur le site du projet correspondant et récupère une release. Il arrive fatalement un moment où c’est un véritable casse-tête de connaitre la version d’une librairie utilisée en production.

Enfin, il faut gérer les conflits de version. En effet, souvent une librairie va utiliser une certaine version de jQuery et une seconde librairie en utilisera une seconde. Si votre application embarque ces deux librairies, il faudra gérer ce conflit.

Le rôle du gestionnaire de dépendances, c’est :

  • de vous permettre d’avoir une visibilité immédiate sur les composants dont dépend votre application et pour chaque composant de connaitre la version utilisée
  • télécharger pour vous les composants externes dans les versions que vous aurez spécifiées et les copier au bon endroit
  • vous remonter les conflits de version et vous aider à les résoudre

Dans Yeoman, le gestionnaire de dépendances, c’est bower. Cet outil a récemment fait l’objet d’un article ici.

Automatisation des tâches

Dans le cycle de vie du développement d’un projet, il y a toujours des taches récurrentes. Par exemple, quelle que soit la technologie utilisée, on va récupérer des sources et les packager sous un format attendu en production. Au préalable, on va sans doute lancer des tests, peut être aussi qu’on va compiler le code écrit en TypeScript/CoffeScript en Javascript. Toutes ces taches ont en commun qu’elles sont algorithmiques et peuvent donc être aisément automatisées. C’est ce à quoi vont nous servir les “tasks runner” .

Les implémentations peuvent changer selon les outils mais généralement, il s’agira d’écrire des taches unitaires :

  • copier un fichier,
  • remplacer dans un fichier, une chaîne de caractères donnée par une autre
  • supprimer le contenu d’un répertoire
  • etc.

Chaque projet pourra constituer son propre workflow en sélectionnant les taches qui l’intéressent.

L’outil embarqué par défaut dans Yeoman à cet effet, c’est Grunt.

Comment fonctionne Yeoman

Les générateurs Yeoman

Il existe un certain nombre de générateurs disponibles ici. Les développeurs Java seront sans doute intéressés par le relativement récent mais très populaire générateur jhipster. Ce générateur propose de générer un projet Maven – Spring – AngularJS en conservant Maven pour le côté serveur et en s’appuyant sur le triplet Yeoman – Bower – Grunt pour le client.

Au-delà des générateurs “officiels”, il existe une multitude de générateurs créés et maintenus par la communauté ici. Mais si vous ne trouvez pas votre bonheur, vous pouvez toujours créer le vôtre. L’ambition de Yeoman, ce n’est pas d’être un outil qui fait tout mais de fournir un cadre permettant la création de générateurs spécialisés sur un même model. Yeoman propose d’ailleurs un générateur de générateur. Il s’agit de faciliter l’écriture d’un nouveau générateur.

Et Yo, à quoi sert-il finalement ?

Plus haut, en présentant le projet Yeoman, j’avais dit qu’il était bâtit autour de trois projets Bower, Grunt et Yo. Cependant, en énumérant les fonctionnalités de Yeoman, j’ai fait ressortir les rôles de Bower et Grunt en passant Yo sous silence. Nous allons combler ce vide.

Yo, c’est le composant qui va exécuter vos générateurs. C’est l’outil qui vous permet depuis la ligne de commande de pouvoir invoquer un générateur par son nom. Par exemple, quand en ligne de commande, je fais “yo angular”, j’exécute yo en lui passant en paramètre le nom du générateur à appeler, ici angular.

Le workflow Yeoman

Au final, ce que Yeoman apporte, ce n’est ni la gestion des dépendances, ni le task runner, ni même la génération du squelette de l’application. Des outils réalisant ces fonctions existaient avant Yeoman et comme indiqué précédemment Grunt, Bower et Yo sont des projets indépendants. Ce que Yeoman apporte au développeur front-end, c’est surtout un process automatisé :
Par exemple, au démarrage d’un nouveau projet,

  • on génère une arborescence de fichiers et de répertoires grâce à Yo et au générateur invoqué. Dans les fichiers générés, il y a la liste des dépendances attendue par Bower et une liste minimale de tâches pour Grunt.
  • On récupère les dépendances grâce à Bower
  • On exécute les taches de build, de tests, de création de package et de déploiement grâce à Grunt

Comme, on peut le voir, au final les implémentations utilisés ne sont pas déterminantes dans ce workflow même si par défaut Yeoman fonctionne avec Bower/Npm et Grunt.

Yeoman par la pratique

Je vous propose de générer un projet AngularJS avec Yeoman afin de voir comment fonctionne l’outil. Dans ce qui va suivre, je vais supposer que Node.js et npm, le gestionnaire paquet de Node.js, sont installés.

Installation de Yeoman

Yeoman étant un module de NodeJs, il s’installe comme suit :


npm install -g yo

Créer le dossier du projet et s’y placer

Il nous faut maintenant créer un dossier correspondant à notre application et nous positionner dans ce dossier. Dans notre cas, le dossier s’appellera “angular-tutorial”. J’ai réutilisé les screenshots de mon article sur AngularJS, c’est pour cette raison que vous voyez des copies du projet “angular-tutoriel” au lieu de “yeoman-tutoriel”.

Ne pas oublier que les générateurs sont des modules Node.js

A partir de là, la seule chose que nous avons à faire c’est :


 yo <nom de mon générateur>

Il faut garder en tête que les générateurs, écrits en Javascript et s’exécutant côté serveur, sont avant tout des modules de Node.js. Avant d’utiliser un générateur, vous devrez donc l’installer via la commande “npm install” comme il est d’usage. Pour vous en convaincre, saisissez la commande


 yo tartempion

Vous devrez avoir l’erreur suivante :

tuto-angular-unknown-generator

Cela signifie simplement qu’aucun module “tartempion” n’est installé. Si le cœur vous en dit vous pouvez toujours l’installer via la commande :


 npm install tartempion

Cependant, je ne garantis pas le succès de l’opération 🙂 .

Rechercher un générateur

Si vous n’êtes pas arrivé à installer le générateur tartempion (et même dans le cas contraire), je vous propose de rechercher un générateur qui correspond à votre besoin. Par exemple imaginez que vous recherchez un générateur permettant de créer un projet incluant maven. Vous pouvez le rechercher via l’outil de recherche mis à disposition par yeoman. Il suffit de saisir la commande suivante :


 yo

Ensuite, suivez les instructions de l’assistant.

tuto-angular-search-generator

Exécution du générateur angular

Maintenant que nous avons compris le fonctionnement de la commande “yo”, nous allons enfin créer notre projet “angular_tutoriel”. Pour cela, tout en restant dans notre répertoire “angular_tutoriel”, nous allons lancer la commande :


 yo angular

Ne vous inquiétez pas si ça semble un peu long. Yeoman va télécharger plusieurs fichiers dont certains sont relativement volumineux. En effet, Yeoman télécharge non seulement les dépendances de votre application mais également tous les outils nécessaires au développement. C’est le cas de PhantomJS un navigateur qui tourne en tache de fond et qui sert à exécuter les tests tout en continuant à profiter de son écran. Le seul PhantomJS fait plus de 12 MO.

L’intégration de Bower

De prime abord, on est agréablement surpris par la convivialité de Yeoman. Dès lors que je lui fournis en entrée le type de projet souhaité, ici AngularJS, il me propose les librairies qui sont susceptibles de m’intéresser. Dans l’écran ci-dessous vous pouvez voir que j’ai coché Bootstrap, angular-route et angular-resource.

tuto-angular-creation-projet

Mes choix seront automatiquement insérés dans le fichier bower.json qui recense toutes les dépendances de mon application. C’est ce fichier qui est utilisé par Bower pour gérer les dépendances. Puisque nous parlons de dépendances, allons directement à la racine pour voir à quoi ressemble le fichier /bower.json généré :


{
  "name": "angular-tutoriel",
  "version": "0.0.0",
  "dependencies": {
    "angular": "1.2.16",
    "json3": "~3.3.1",
    "es5-shim": "~3.1.0",
    "bootstrap": "~3.2.0",
    "angular-resource": "1.2.16",
    "angular-route": "1.2.16"
  },
  "devDependencies": {
    "angular-mocks": "1.2.16",
    "angular-scenario": "1.2.16"
  },
  "appPath": "app"
}

Il est intéressant de noter que je n’ai pas eu à préciser que j’avais besoin d’Angular. Dès lors que j’ai indiqué Angular comme type de projet, la dépendance Angular sera rajoutée. On peut voir aussi que les librairies nécessaires mais auxquelles je n’avais pas pensées “es5-shim” (compatibilité pour les vieux navigateurs) et “json3” ont été directement rajoutées aux dépendances.

Faisons un détour vers le fichier /app/index.html . On peut y voir que Yeoman est allé jusqu’à m’insérer les imports de ressources javascript et css.


    <!-- build:js(.) scripts/vendor.js -->
    <!-- bower:js -->
    <script src="bower_components/jquery/dist/jquery.js"></script>
    <script src="bower_components/angular/angular.js"></script>
    <script src="bower_components/json3/lib/json3.js"></script>
    <script src="bower_components/bootstrap/dist/js/bootstrap.js"></script>
    <script src="bower_components/angular-resource/angular-resource.js"></script>
    <script src="bower_components/angular-route/angular-route.js"></script>
    <!-- endbower -->
    <!-- endbuild -->

    <!-- build:js({.tmp,app}) scripts/scripts.js -->
    <script src="scripts/app.js"></script>
    <script src="scripts/controllers/main.js"></script>
    <script src="scripts/controllers/about.js"></script>
    <!-- endbuild -->

Arborescence du projet généré

tuto-angular-structure-projet

Il est intéressant de noter que :

  • Yeoman m’a généré de facto un dossier de test, au cas je serais tenté de ne pas en créer
  • Un fichier git ignore a été généré. Ce fichier exclut par défaut les fichiers Bower-Grunt-Yo

Le fichier package.json

Revenons à l’arborescence du projet et intéressons-nous au fichier package.json créé par Yeoman. Ce fichier est bien connu dans l’univers Node.js. Il permet entre autres de décrire un module à l’aide de métadonnées telles que son nom et sa version. Voici le contenu de notre fichier /package.json


{
  "name": "angulartutoriel",
  "version": "0.0.0",
  "dependencies": {},
  "devDependencies": {
    "grunt": "^0.4.5",
    "grunt-autoprefixer": "^0.7.3",
    "grunt-concurrent": "^0.5.0",
    "grunt-contrib-clean": "^0.5.0",
    "grunt-contrib-concat": "^0.4.0",
    "grunt-contrib-connect": "^0.7.1",
    "grunt-contrib-copy": "^0.5.0",
    "grunt-contrib-cssmin": "^0.9.0",
    "grunt-contrib-htmlmin": "^0.3.0",
    "grunt-contrib-imagemin": "^0.7.0",
    "grunt-contrib-jshint": "^0.10.0",
    "grunt-contrib-uglify": "^0.4.0",
    "grunt-contrib-watch": "^0.6.1",
    "grunt-filerev": "^0.2.1",
    "grunt-google-cdn": "^0.4.0",
    "grunt-newer": "^0.7.0",
    "grunt-ngmin": "^0.0.3",
    "grunt-svgmin": "^0.4.0",
    "grunt-usemin": "^2.1.1",
    "grunt-wiredep": "^1.7.0",
    "jshint-stylish": "^0.2.0",
    "load-grunt-tasks": "^0.4.0",
    "time-grunt": "^0.3.1"
  },
  "engines": {
    "node": ">=0.10.0"
  },
  "scripts": {
    "test": "grunt test"
  }
}

L’objet “devDependencies” du fichier “package.json” contient tous les modules Node.js nécessaires au développement mais qui ne seront pas dans le package envoyé en production. On retrouve plusieurs dépendances du type “grunt*”. Ce sont des taches Grunt.

Quid des caractères spéciaux dans les numéros de version

Vous avez du remarquer des caractères spéciaux dans les numéros de version au niveau des fichiers bower.json et package.json. Ils méritent qu’on s’y attarde.

Le caractère ‘^’

Si nous indiquons ‘^1.3.1’, cela signifie que le gestionnaire de version peut prendre la dernière version du moment qu’elle est de la forme ‘1.*’. Par exemple il pourrait prendre la ‘1.10.1′ ou la ‘1.11.1′. En revanche, il ne pourra pas prendre la version ‘2.0.1′.

Le caractère ‘~’

Si nous indiquons ‘~1.3.1’, cela signifie que le gestionnaire de version peut prendre la dernière version du moment qu’elle est de la forme ‘1.3.*’. En clair, il s’agit de ne prendre que les patchs d’une version donnée. Par exemple il pourrait prendre la ‘1.3.1′ ou la ‘1.3.4′ mais pas la ‘1.4.0′ ou la ‘2.0.0′.

attentionBien que cette syntaxe puisse être très utile, elle peut être la cause de bugs difficiles à repérer et à reproduire puisque dans la même journée un projet open source peut être patché plusieurs fois. Mon avis personnel est de préférer des montées de versions manuelles validées par une série de tests.

Intégration avec Grunt

Si nous regardons à nouveau le dossier de notre “angular-tutoriel”, nous pouvons noter la présence du fichier GruntFile.js. Il s’agit du fichier utilisé par Grunt pour identifier les tâches qu’il doit exécuter. Toujours à partir de la racine du projet, saisissons la commande suivante :


 grunt serve

Au final, vous devriez avoir :
tuto-angular-grunt-serve

Les logs de Grunt parlent d’eux-mêmes. Il est aisé de voir que Grunt a constitué le package à déployer puis a démarré le serveur avec un mécanisme de rechargement à chaud. Normalement, Grunt a dû lancer un onglet de navigateur avec votre application. Si ce n’est pas le cas, consultez la page la page https://localhost:9000/#/. Vous devriez avoir ceci :

tuto-angular-execution

Conclusion

Comme on peut le voir Yeoman, ne se contente pas de créer un squelette vide. Nous avons dès le départ une application fonctionnelle et qui est déployée. Notez que grâce à Node.js et à Grunt, je n’ai pas eu à installer un serveur web. Pour cette phase de développement, Node.js m’en fournit un.

Venant du monde Java et habitué de Maven, j’ai été agréablement surpris de voir qu’un outil tel que Yeoman existe dans l’univers front-end.