Accueil > Web > Créez vos tâches personnalisées avec Grunt

Créez vos tâches personnalisées avec Grunt

Introduction

Dans les deux précédents articles de cette série, nous avons effectué un tour théorique de Grunt et nous avons ensuite mis en place un workflow personnalisé du build d’un projet en utilisant des plugins (ou tâches) Grunt déjà existants. Nous allons maintenant aller plus loin dans la prise en main de Grunt et créer nos propres tâches personnalisées !

Attention ! Dans l’article précédent, nous avions créé des tâches (notamment la tâche build), mais celles-ci n’étaient alors qu’un alias pour exécuter plusieurs autres tâches. Ce que je vous propose à présent, c’est de pouvoir mettre en place de nouvelles tâches lorsque vous ne trouvez pas de plugin pouvant répondre à votre besoin. Comme fil rouge de cet article, nous allons réaliser une tâche Grunt qui servira à définir des variables de configuration suivant l’environnement sur lequel nous souhaitons livrer notre projet.

Imaginons en effet que nous ayons deux environnements sur lesquels nous souhaitons livrer, par exemple un environnement de pré-production et un environnement de production. Imaginons que, pour une raison quelconque, l’adresse courante ait besoin d’être renseignée dans un fichier de configuration. Comme les différents environnements sont situés à des adresses différentes, il peut être intéressant d’automatiser le remplacement de ces paramètres dans une tâche Grunt.

A travers cet exemple, nous allons voir deux façons de déclarer des tâches Grunt, une simple puis une plus évoluée.

Tâches simples

Mise en place

La première méthode de définition d’une tâche que nous allons aborder est la plus basique et conviendra pour des tâches spécifiques destinées à être peu réutilisées. Mais voyons la syntaxe :

On déclare donc notre tâche en spécifiant son nom, sa description, et la fonction correspondante. Les arguments correspondent à ceux donnés lorsqu’on appelle la tâche : grunt maTache:arg1:arg2. Ceux-ci sont bien entendus facultatifs, mais dans notre cas nous allons en utiliser un qui représentera l’environnement pour lequel nous voulons déclarer nos variables de configuration.

Notre tâche récupérera la liste des variables à remplacer, puis parcourra les fichiers pour remplacer celles-ci.
Cette tâche interviendra à la fin du build de l’article précédent, lorsque les fichiers auront déjà été copiés dans le dossier dist. Je vais utiliser ici plusieurs fonctions de l’API Grunt que je vous engage vivement à consulter.

Le plus propre est de stocker vos tâches personnalisées dans un dossier (que nous appelerons ici tasks) puis de les charger via grunt.loadTasks(path);
Créons donc un fichier tasks/replace-config-vars.js que nous remplirons avec le code minimal :

Puis chargeons les tâches dans notre fichier Gruntfile.js avec grunt.loadTasks('tasks');

Pour séparer notre tâche de façon plus logique, nous allons créer deux fonctions dont le but est respectivement de collecter les remplacements à effectuer à partir d’un fichier .json et de remplacer ces termes dans un fichier précis. Créons par exemple ce fichier config.json à la racine de notre projet :

Pour vérifier que notre tâche fonctionne, je vous invite également à rajouter cette ligne dans notre index.html :

Chargement des remplacements

Créons maintenant une première fonction, chargée de récupérer les remplacements à effectuer dans notre fichier .json en tenant compte de la cible (prod ou preprod). Nous utiliserons des expressions régulières pour effectuer les remplacements multiples et n’oublierons pas d’interrompre la tâche s’il y a le moindre problème. Voici le code de cette fonction :

openString et closeString permettent de définir le format des tags à remplacer, ici nous utiliserons une syntaxe du style @@variable. Pour le reste, il s’agit essentiellement de javascript simple et de l’utilisation de l’API Grunt fournie, que je vous conseille une fois de plus de consulter.

Remplacer dans un fichier

Maintenant, nous allons réaliser la fonction permettant d’effectuer ces remplacements dans un fichier passé en paramètres. Celle-ci est très simple, maintenant que nous avons notre tableau de chaînes à remplacer :

Notre tâche principale

Comme on peut le voir, l’API Grunt nous simplifie bien la vie. Passons maintenant à la définition de la tâche elle-même :

Et voilà notre tâche prête à être lancée ! Rajoutons-là à notre tâche build des articles précédents, que nous allons transformer en fonction pour pouvoir propager la cible. Profitez-en pour créer un fichier tasks/build.js qui lui est dédié.

Et vous pouvez maintenant lancer votre build de trois façons différentes :

Le code source complet à ce stade peut être trouvé sur cette branche du dépôt Git.

Améliorations

Notre tâche est fonctionnelle, mais il y a plusieurs points qui pourraient être améliorés :

  • Notre tâche effectue obligatoirement les mêmes remplacements sur tous les fichiers. Il serait bien de pouvoir configurer les fichiers qu’on souhaite atteindre, et de spécifier des remplacements différents pour chacun si on le souhaite.
  • De même, il serait utile de pouvoir utiliser des motifs de remplacement différents paramétrables suivant les fichiers, la syntaxe @@variable n’étant pas forcément la plus adaptée à tous les types de fichiers !
  • Actuellement, les fichiers source et destination sont les mêmes. Cela peut être gênant si l’on souhaite utiliser un emplacement temporaire, comme certaines autres tâches le font.

Comme on peut le voir, toutes ces limitations entraînent une seule solution : on souhaite pouvoir configurer notre tâche en changeant les paramètres généraux, mais également en la divisant en sous-tâches ciblant seulement certains fichiers ou effectuant des remplacements différents.

Pour cela, les tâches simples suffiraient (à renfort de bricolage avec les arguments), mais ne sont pas les plus adaptées ; pour cela, nous allons utiliser un second type de tâches, les tâches multiples.

Tâches multiples

Mise en place

Les tâches multiples diffèrent des tâches simples sur un point particulier : vous pouvez diviser votre tâche en sous-tâches. Grunt appelle cela des targets, ce qui permet d’utiliser des paramètres différents. Lors de l’appel de la tâche, Grunt ira vérifier qu’une configuration correspondant au nom de la tâche est bien définie. Les tâches se lancent ainsi : grunt tâche:target. En appelant simplement grunt tâche, la tâche sera exécutée pour toutes les targets. Attention, il ne s’agit donc plus d’un argument de notre tâche ! Voici comment on déclare désormais notre tâche multiple :

Que pourrait représenter une target dans notre tâche ? On peut par exemple s’en servir pour effectuer des remplacements différents suivant les types de fichiers. L’une pourrait ainsi être appelée js pour cibler uniquement les fichiers javascript, une autre css pour cibler uniquement les fichiers CSS, etc.

Comme vous pouvez le constater, nous ne pouvons plus préciser l’environnement sous la forme grunt tâche:environnement, puisque dans les tâches multiples cette syntaxe est réservée aux target. Nous allons plutôt utiliser une option en lançant notre tâche build de la façon suivante :

Modifions ainsi la récupération de l’environnement dans notre tâche :

Rendre notre tâche paramétrable

Nous allons avoir plusieurs éléments configurables dans notre tâche :

  • le fichier des variables de configuration (par défaut on cherchera un config.json) ;
  • la chaîne d’ouverture d’un tag (par défaut, ‘@@’) ;
  • la chaîne de fermeture d’un tag (par défaut, ”) ;
  • les fichiers dans lesquels nous allons remplacer les tags ;
  • les destinations de ces mêmes fichiers ;

Nous verrons les deux derniers points dans un second temps, car ils sont gérés directement par Grunt pour nous. Pour les trois premiers, nous allons utiliser this.options() qui nous permet de récupérer les configurations en spécifiant des valeurs par défaut. Nous pourrons ainsi utiliser ces options directement dans la suite de notre code.

Pour les fichiers à modifier, comme déjà évoqué dans le deuxième article de la série, Grunt possède une très bonne couche d’abstraction pour la gestion des fichiers. Grâce à cela, nous avons juste à récupérer this.files dans notre tâche pour obtenir la liste des fichiers et de leur destination telle que définie dans la configuration (cf. initConfig).

Nous allons donc commencer par rajouter un argument à notre fonction replaceInFile() pour prendre en compte la destination. Attention cependant, il faut savoir que dans le format retourné par this.files la destination peut être un répertoire, auquel cas il va nous falloir le transformer en chemin vers un fichier pour qu’il soit accepté par grunt.file.copy. Voici le nouveau code de notre fonction :

Il ne nous reste plus qu’à modifier notre tâche en elle-même pour utiliser les fichiers contenus dans this.files et non plus la fonction grunt.file.recurse. Il faut cependant savoir deux autres choses sur le format de this.files, liées au fait que this.files[x].src est un tableau :

  • il est possible que src ne contienne aucun fichier, auquel cas on ne doit pas traiter l’itération ;
  • il est possible que src contienne plusieurs fichiers, ce qui est utile pour les tâches où l’on souhaite concaténer plusieurs fichiers ou si la destination est un répertoire. Ici, cela n’a aucun sens, on annule donc l’itération si et seulement si la destination est un fichier unique.

Voici par conséquent notre tâche finale :

Avec la tâche build adaptée :

Vous pouvez à présent utiliser des configurations différentes selon vos fichiers. Par exemple :

Conclusion

Le code final du projet est disponible sur cette branche du dépôt Git.

Au cours de ces trois articles, nous avons pu découvrir que Grunt vous permet de définir des workflows automatisés  simples et configurables. Les possibilités sont nombreuses, que ce soit grâce au grand nombre de plugins disponibles (la communauté étant très active) ou grâce à la possibilité de créer soi-même des tâches.

Un des autres avantages de la communauté est qu’il n’est pas rare de trouver des templates de projets ayant déjà leurs workflows Grunt préconfigurés. C’est le cas avec Yeoman dont les projets générés contiennent souvent leur propre Gruntfile.

Categories: Web Tags: , , ,
  1. Pas encore de commentaire
  1. Pas encore de trackbacks


cinq − 1 =