Theme WordPress + Gulp

Développer des thèmes WP avec Gulp

Cet article a été réactualisé il y a 2 ans jours. Il n'est pas nécessairement obsolète, mais gardez son ancienneté en tête lors de sa lecture.

Lors d’un développement de thème pour WordPress en local, pas mal de tâches peuvent être automatisées pour se faciliter la vie et gagner du temps.

Les tâches récurrentes de développement de thèmes

En développant un thème sous WP, plusieurs tâches front-end reviennent de façon répétitives :

  • Au niveau des feuilles de styles :
    • Compiler du sass en css, et générer un sourcemap
    • Ajouter les préfixes vendor
    • Minifier les feuilles de style
  • Au niveau des scripts JS
    • Concaténer des scripts
    • Minifier ses scripts
    • Surveiller les éventuelles erreurs d’écriture
  • Au niveau des images
    • Optimiser le poids des images
  • Au niveau des traductions
    • Générer un fichier .pot
  • De façon globale
    • Respecter les coding standards
    • Organiser son travail avec des fichiers sources servant au développement, et générer des fichiers pour la mise en production
    • Utiliser un rafraichissement automatique du navigateur lors de la modification des fichiers
  • Etc …

Que permet Gulp ?

Avant de découvrir Gulp, j’avais automatisé certaines de ces tâches précédemment citées dans l’éditeur que j’utilise le plus souvent : Sublime Text3, en créant des builds, en ajoutant des fonctionnalités via des packages etc …
Pour autant, je ne trouvais pas la chose très simple, ni vraiment performante, en tout cas pour moi ! De plus, les configurations étant implémentées dans Sublime, je me retrouvais dépendante de cet éditeur particulier et de surcroît sur un pc particulier. Il était temps de sauter le pas pour intégrer Gulp à mon environnement de travail !

Gulp est un « Task runner » ou Automatiseur de tâches. Ce n’est qu’un outil, il ne fournit que la base pour une automatisation de tâches, à lui seul c’est une coquille vide. Le travail qu’on va lui demander d’éxécuter réside dans les plugins développés pour lui. Gulp permet, via des fichiers de configuration qu’on met en place, notamment un fichier gulpfile.js, de lancer des scripts provenant de bibliothèques externes (les plugins dont on vient de parler et qui sont présents sur npm ) pour effectuer des tâches plus ou moins complexes pour modifier ses fichiers sources comme sass, js images etc …, et ce, de façon automatique. On peut créer ses propres plugins, mais avec tout ce qui est déjà disponible, ce dont on aura besoin existe sûrement déjà. Inutile de perdre du temps à réinventer la roue !

Gulp doit être installé en global sur sa machine pour pouvoir utiliser la commande gulp depuis n’importe où sur son PC. Il permet d’automatiser des tâches comme par exemple le rafraîchissement automatique de son navigateur (avec browserSync), du lint sur du JS, pour n’importe quel projet ouvert dans un éditeur. Gulp peut aussi être ajouté en local dans un projet (dans une version qui pourra être différente de celle de l’installation globale) comme nous allons le faire ici dans un thème WP.

Préalables à l’utilisation de Gulp

Pour pouvoir utiliser Gulp, il faut que Node.js soit installé sur sa machine.

NodeJS est un environnement d’éxécution Javascript qui propose de nombreux modules pour composer son architecture logicielle. Etant pour ma part sous Windows, l’installation est très simple, télécharger l’installeur NodeJS : sous Windows = un fichier en .msi, double cliquer dessus et suivre les instructions.

En installant Node, on a accès à npm (node package manager) qui est le gestionnaire des modules (paquets) proposés par NodeJS. Il faut voir npm un peu comme l’équivalent d’apt-get sous Linux pour installer des programmes. Une simple commande npm suivi du nom d’un package et le module est téléchargé et exploitable.

On va pouvoir utiliser npm pour installer Gulp :

Pour pouvoir lancer Gulp en ligne de commande, il faut l’installer de façon globale (ajout du flag -g)

npm install gulp-cli -g

Quelle différence entre gulp et gulp-cli ?

Vous pourrez voir dans différents tutoriels que l’on préconise d’installer gulp en global en tapant  npm install gulp -g,  et parfois npm install gulp-cli -g. En fait, ce sont deux méthodes d’installation différentes. Il est recommandé maintenant d’installer gulp-cli plutôt que gulp.

gulp-cli est un programme utilitaire qui vous permet d’accéder à gulp à partir de votre shell. Installer gulp-cli en global au lieu de gulp permet d’utiliser gulp comme un programme global mais en donnant la possibilité d’utiliser des versions différentes de gulp dans des projets …

Pas clair ? Ne vous inquiétez pas j’ai mis du temps à comprendre !!!  Plus d’infos sur ces liens : https://github.com/gulpjs/gulp/blob/master/docs/getting-started.md et https://medium.com/gulpjs/gulp-sips-command-line-interface-e53411d4467

Il s’agit donc d’une histoire de version de gulp installée en global sur le PC qui sans gulp-cli prendrait le dessus sur des versions de gulp utilisée dans des projets. Donc si vous aviez précédemment installé globalement gulp, il va falloir le supprimer npm rm --global gulp pour installer gulp-cli npm install gulp-cli -g

Quels sont les dossiers/fichiers présents dans un projet de thème utilisant Gulp ?

En plus des fichiers basiques de WordPress : header.php, index.php, footer.php, style.css, functions.php.

On aura :

  • package.json : fichier qui contient les spécifications du projet, à savoir au minimum son nom et sa version, ainsi que la liste des plugins gulp nécessaires à la mise en place de tâches. On peut l’écrire à la main, ou le générer automatiquement via la commande npm init
  • package-lock.json : fichier qui spécifie et fixe les versions de tous les plugins gulp qu’on a téléchargé via npm. Il se génère automatiquement, on ne l’écrit pas à la main.
  • node_modules : dossier contenant tous les modules (ainsi que leur dépendances) dont le projet a besoin. Il se génère automatiquement. Il pèse très vite lourd … Il ne faut pas le versionner !
  • gulpfile.js : fichier de configuration des différentes tâches qu’on souhaite exécuter. Ce fichier là est à écrire soi même, on y met ses directives.

Mise en place de Gulp dans un thème

Pour faciliter la compréhension de la structure des fichiers qui vont composer le thème, je vais utiliser le Starter theme Underscores, et le modifier juste ce qu’il faut pour qu’il intègre la gestion de certains de ses fichiers via Gulp. Vous pouvez retrouver le repository de ce starter thème adapté que j’ai nommé « Gulpy » sur mon compte Github.

Etape 1 : Modifier la structure du thème

Pour utiliser Gulp dans un développement de thème il n’existe pas une seule manière de procéder mais autant de façon de faire que de développeurs et de besoins ou d’habitudes ! Je vous montre donc ce que j’ai choisi comme mise en place pour le moment.

Le principe consiste à faire en sorte de prendre certains des fichiers présents dans le thème pour les placer dans un dossier qui servira de ressources de développement : dossier /src.

Ce dossier /src va contenir les /assets , à savoir :

  • les fichiers sass, (ou less ou stylus) qui seront « surveillés » pour les compiler en css à chaque modification
  • les images que l’on peut considérer comme « brutes » qui pourront bénéficier d’une optimisation
  • les scripts js qui peuvent provenir de librairies externes, ou qu’on récupère d’un thème, ou qu’on a ajoutées soi même. On pourra du coup les linter, les minifier et concaténer ceux qui peuvent l’être.
  • les fonts
  • les icones

Ce dossier /src constitue les ressources de développement qui vont me permettre de générer un nouveau dossier /assets pour la prod cette fois à la racine de mon thème. Le dossier /src ne figurera pas sur le serveur de prod.

Je n’utilise pas de dossier /dist pour placer mes nouveaux fichiers générés comme on peut généralement le voir comme pratique. Dans le cas d’un développement de thème wp ici, je préfère simplifier les chemins (path) à ajouter dans les fichiers pour que tout fonctionne comme si on n’utilisait pas de structure particulière pour utiliser Gulp.

Une fois qu’on a déplacé les fichiers dans ce dossier /src, il faut prendre soin de modifier l’appel des différents fichiers au niveau du functions.php, et ailleurs si nécessaire pour aller les chercher dans le dossiers /assets qui va être généré par Gulp pour la prod. En gros, voilà à quoi ressemble mon architecture de thème. (NB: Les dossiers marqués d’une astérisque *, sont autogénérés.)

gulpy
|/assets *
|-/css *
|-/fonts *
|-/img *
|-/js *
|/src
|-/assets
|-|-/fonts
|-|-/img
|-|-/js
|-|-/sass
|/header.php
|/index.php
|/footer.php
|/style.css
|/functions.php

Etape 2 : Créer le fichier des dépendances : package.json

Ouvrir une console dans son nouveau thème et taper npm init

Cette commande va permettre de générer le fichier package.json de façon automatique.  Cela va initialiser un questionnaire pour le finaliser. Les infos sont demandées dans la console, à savoir : le nom du projet, sa version, etc …

Si on ne veut pas passer par le questionnaire on peut ajouter le flag –yes npm init --yes, du coup les infos seront extraites automatiquement du document courant.

npm init n’est pas obligatoire, on peut tout à fait créer le package.json à la main (mais c’est plus long !) ou récupérer un package.json d’un autre projet (cf Gulpy sur Github).

Etape 3 : Déclarer Gulp comme dépendance de développement dans le projet

Les dépendances peuvent être :

  • des dépendances uniquement pour le développement (par exemple compiler son sass), dans ce cas la commande à lancer aura une écriture de ce type :
    • npm install <package_name> --save-dev
    • ou npm install --save-dev <package_name>
    • ou npm i -D <package_name>
  • ou des dépendances pour le projet (par exemple récupérer normalize.css)
    • npm install <package_name> --save
    • ou npm install --save <package_name>
  • on peut aussi installer plusieurs packages à la fois : npm install <package1> <package2> <package3> --save-dev

En lançant une commande d’ajout de dépendance, la dépendance va automatiquement venir s’ajouter au fichier package.json que l’on a créé juste avant. Voici un aperçu de ce à quoi cela ressemble :

{
  "name": "my_package",
  "version": "1.0.0",
  "dependencies": {
    "my_dep": "^1.0.0"
  },
  "devDependencies" : {
    "gulp": "^3.5.6"
  }
}

Ici nous n’allons utiliser que des dépendances de développement pour travailler sur le thème, donc des « devDependencies ».

Ajouter la dépendance Gulp : npm install gulp --save-dev 

Ici gulp aura une version qui peut être totalement différente de celle installée en global sur sa machine (d’où l’intérêt d’avoir installé gulp-cli en global pour que la version dans le projet soit bien prise en compte)

Plusieurs choses se sont passées :

  • Le package.json s’est modifié pour ajouter la dépendance gulp
  • Un package-lock.json s’est créé
  • Un dossier node_modules s’est créé (mais pas visible sous Sublime texte, est exclu d’emblée dans la configuration pour ne pas plomber la recherche dans les fichiers du projet). C’est lui qui va conserver toutes les dépendances du projet. => Si on utilise Git : A ne surtout pas versionner ! Inscrire node_modules dans le fichier .gitignore.

NB : Si un package.json est déjà présent dans un projet, il suffit de faire un npm install sans préciser quoique ce soit d’autre pour créer le package-lock.son et le dossier node_modules.

Etape 4 : Créer le fichier des tâches à effectuer : gulpfile.js

Le fichier des tâches qui seront à mettre en œuvre se nomme gulpfile.js. On le crée soi même. Ici on va commencer par déclarer notre base, l’utilisation de Gulp.

var gulp = require('gulp');

De quelles tâches a t’on besoin ?

Pour simplifier le développement d’un thème WordPress il existe différentes tâches récurrentes qui peuvent être automatisées, nous l’avons vu plus haut. Des dépendances permettent de s’en occuper pour nous.
Les dépendances (ou modules) sont à notre disposition sur npm : https://www.npmjs.com/ Consulter ce dépôt nous permet de les chercher, puis la documentation pour chacun des modules va permettre de savoir comment s’en servir. (A noter qu’on peut aussi utiliser directement des dépôts git sans passer par npm en indiquant l’url du dépôt voir la doc npm sur install)

Voici quelques tâches bien utiles et des modules qui permettent de s’en charger. (NB: Vous pouvez choisir d’attribuer vos tâches à d’autres modules qui font les choses de façon similaire.)

  • Compilation Préprocesseurs Sass gulp-sass
  • Utiliser SourceMap sur le css compilé gulp-sourcemaps
  • Renommer des fichiers gulp-rename
  • Filtrer des fichiers gulp-filter
  • Autoprefixer CSS gulp-autoprefixer
  • Minification des fichiers CSS gulp-clean-css
  • Rafraîchissement automatique de la page dans le navigateur à chaque modifications apportées et navigation synchronisée entre appareils browser-sync
  • JS Lint : détection d’erreur JS gulp-jshint
  • Récupérer un rapport d’erreur jshint formaté et coloré jshint-stylish
  • Minifier les fichiers JS gulp-uglify
  • Concaténer les fichiers JS gulp-concat
  • Optimisation des images en png, jpeg, gif et svg gulp-imagemin
  • Générer un fichier .pot pour effectuer les traductions des chaines get text présentes dans les templates wp du thème. gulp-wp-pot
  • Forcer la lecture des fichiers dans un ordre spécifique gulp-sort

Ajouter ces dépendances une à une ou toutes d’un coup !

Créer les tâches

On va demander à notre serveur JS de bosser pour nous avec les dépendances qu’on a mises à sa disposition.

Déjà pour utiliser les dépendances il va falloir, comme on l’a fait pour gulp, indiquer au début du fichier gulpfile.js les modules requis en déclarant des variables de modules. On nomme ces variables de façon a bien les identifier avec des noms explicites.

Par exemple pour utiliser gulp-sass on écrira :

var sass = require('gulp-sass');

Ensuite on pourra utiliser les fonctions mises à disposition par le module dans la tâche.

Voici à quoi ressemble une tâche :

JS

L’API Gulp contient plusieurs fonctions dont les 4 principales sont :

  • gulp.task permet de définir une tâche en lui donnant un nom, éventuellement un tableau de dépendance à d’autres tâches, et une fonction de rappel qui sera appelée lors de l’exécution de la tâche. 
    ‘truc’ est ici un exemple de nom donné à la tâche : son nom est au libre choix du développeur, on fait en sorte de choisir un terme explicite.
  • gulp.src permet d’indiquer les sources à utiliser : on y ajoute le chemin vers le fichier entre parenthèses. gulp.src utilise .pipe Les « pipe » sont des tunnels permettant de faire des opérations. Ici on passe le nom des fonctions à utiliser, se référer aux documentations des modules sur npm pour les connaitre.
  • gulp.dest On « pipe » une dernière fois, pour mettre tout le boulot effectué dans le fichier de destination via gulp.dest qui contient le chemin vers le dossier ou fichier dans lequel il faut envoyer les résultats du script
  • gulp.watch permet de surveiller si des changements interviennent dans les fichiers, si oui, on déclenchera des tâches particulières.

Ce que l’on doit comprendre ici c’est que pour effectuer les tâches, on doit bien avoir formalisé l’arborescence de son thème. On travaille sur des fichiers sources, et on envoie dans des fichiers de destination le résultat des processus qu’on a demandé. Voilà pourquoi j’ai mis en place un dossier /src, ça permet de ne pas tout mélanger.

Comment lancer les tâches ?

Dans la console il suffit de taper gulp suivi du nom de la tâche.

Dans l’exemple au dessus, on a définit une tâche 'truc' : On peut donc lancer dans la CLI gulp truc pour l’exécuter.

On peut voir aussi la présence d’un return au début de la tâche, à quoi ça sert ?

Node.js  travaille de manière asynchrone. Sans demander un retour (ajouter return), si plusieurs tâches doivent être effectuées, Gulp n’attend pas la fin d’une tâche avant d’en exécuter une autre. Il passe immédiatement à la tâche suivante sans attendre que la précédente soit terminée. Ça devient embêtant quand des tâches sont dépendantes d’autres tâches. Retourner le flux de gulp permet à gulp de savoir qu’il doit attendre que la tâche soit terminée avant d’exécuter les tâches qui en dépendent.

Et justement, dans la tache par défaut de l’exemple précédent, on est dans ce cas. Une tâche par défaut est une tâche que l’on pourra lancer avec le mot clé 'gulp' uniquement (ou gulp default mais bon, pas utile d’en écrire davantage !) Elle renferme le boulot qu’on lui demande de faire : ici pour l’exemple un watch

On a fait en sorte ici qu’une tâche par défaut soit dépendante d’une autre tâche  ici [‘truc’]. Elle ne s’éxécutera alors que si la tâche ‘truc’ est faite au préalable. La partie entre crochet permet donc de déclarer des dépendances à d’autres tâches : « fait la tâche par défaut qui est un watch, mais avant, fait moi la tâche ‘truc' ». On demande à Gulp de faire la tâche ‘truc’ jusqu’à la fin avant de passer à autre chose.

Etape 5 : Ecrire ses tâches dans gulpfile.js

Bon, concrètement ça donne quoi ?

On va se référer à Gulpy, le starter thème que j’ai mis en place en utilisant Underscores visible sur mon compte Github.

Aller voir Gulpy

Ouvrez le fichier gulpfile.js pour suivre le cheminement, je ne vais pas tout recopier ici mais expliquer l’ordre dans lequel se font les choses.

Appeler les modules

On attribue des variables aux modules, leur donner un nom qui a du sens.

S’occuper des styles

  1. Au niveau de src/assets/sass on a une feuille de style principale main.scss qui fait l’import de tous les partials. Ce fichier est donc la source qui doit être convertie en css.
  2. On traite les sources originales pour générer un sourcemap
  3. On compile sass en css
  4. A ce css on ajoute les vendor prefixes
  5. On ajoute le sourcemap aux côtés de la source modifiée
  6. On fait en sorte que les retours chariots sous windows dans le fichier css généré ne posent pas de problèmes
  7. On envoie le résultat à la racine du thème dans un dossier assets/css
  8. On rafraîchit le navigateur pour qu’il prenne en compte les modifs (dans le cas où on utilise la feuille de style main.css)
  9. On récupère les fichiers css présents dans le dossier qui vient d’être créé (bon ici on n’a que main.css)
  10. On minifie tout ça
  11. On renomme en .min
  12. On fait en sorte que les retours chariots sous windows dans le fichier main.min.css généré ne posent pas de problèmes
  13. On envoie le résultat à la racine du thème dans un dossier assets/css aux cotés de main.css
  14. On rafraîchit le navigateur pour qu’il prenne en compte les modifs (dans le cas où on utilise la feuille de style main.min.css)

S’occuper des images

  1. Au niveau de src/assets/img on a des images en png ou jpg ou gif ou svg, elles peuvent être mélangées ou classées dans des dossiers du même nom.
  2. On les récupère toutes en utilisant la syntaxe Globbing
  3. On leur applique une optimisation dont les options sont à voir sur la doc de gulp-imagemin
  4. On envoie le résultat à la racine du thème dans un dossier assets/img

S’occuper des scripts

Ici je découpe les choses, j’ai rangé des scripts dans:

  • src/js/appcustom : Ce sont des scripts qu’on qualifiera de perso, et qui peuvent être mis en enqueue dans functions.php au niveau du footer. Ils peuvent être concaténés sans problèmes. Pas de dépendances entre eux.
  • src/js/condcustom : Ce sont de scripts qu’on mettra en enqueue dans functions.php mais sous certaines conditions. Il y a des JS qu’on voudra mettre dans le header, ou qu’on ajoute juste pour le customizer, on qu’on ne veut charger que sur certaines pages du site.
  • src/js/vendor : Ce sont des scripts qui viennent de librairies externes, on ne leur apportera pas de modifications. Normalement on doit les charger soit avec gulp, ou composer, pour les garder à jour. L’autre point c’est que par exemple html5shiv.js ne devra pas être concaténé avec quoique ce soit d’autre comme script, on doit l’ajouter de façon conditionnelle pour les versions antérieures à IE9. Certains vendors qu’on ira chercher pourraient être concaténés avec nos scripts persos, mais je ne vois pas comment organiser ma structure de dossiers pour faire ça pour le moment.

Tâche sur les appcustom

  1. On récupère les sources dans src/assets/js/appcustom
  2. On les rassemble sous un nom qu’on choisit
  3. On fait en sorte que les retours chariots sous windows dans le fichier appcustom.js généré ne posent pas de problèmes
  4. On envoie le résultat à la racine du thème dans un dossier assets/js
  5. On renomme le fichier généré en appcustom.min.js
  6. On minifie tout ça
  7. On fait en sorte que les retours chariots sous windows dans le fichier appcustom.min.js généré ne posent pas de problèmes
  8. On envoie ce nouveau fichier au côté du précédent

Tâche sur les autres js : condcustom et vendor

  1. On récupère les sources dans src/assets/js/condcustom et src/assets/js/vendor
  2. On fait en sorte que les retours chariots sous windows dans les fichiers ne posent pas de problèmes
  3. On envoie le tout (chaque fichiers séparément) dans assets/js
  4. On les renomme (chacun) en .min
  5. On les minifie
  6. On fait en sorte que les retours chariots sous windows dans les fichiers générés ne posent pas de problèmes
  7. On envoie le tout aux côtés des précédents js non minifiés.

On crée une tâche globale pour les JS

Cette tâche ne pourra se lancer que si les deux précédentes sont totalement effectuées l’une après l’autre. Çà permet aussi d’avoir une commande simplifiée à lancer dans la CLI.

Générer le dossier des assets pour la prod

Les tâches dont on vient de parler peuvent être regroupées en une seule que je nomme ‘build’

gulp.task('build', ['style', 'js', 'images']);

Automatiser tout ça : Créer un watch

A chaque fois qu’on fera des modifications au niveaux des fichiers du thèmes (les src/assets et les php), ce qu’on veut c’est que les tâches se lancent toutes seules, sans avoir à chaque fois à lancer les tâches dans la CLI. Gulp nous permet de le faire facilement.

La tâche watch

  1. On lance browsersync pour avoir un livereload à chaque fois qu’on modifie quelquechose (Pensez bien à modifier l’url proxy pour la vôtre ! ici changer https://gulpify.local)
  2. On surveille tous les scss (les partials aussi) si des modifs interviennent, on relance la tâche style
  3. On fait pareil pour des modifs sur les fichiers php
  4. les js dans appcustom
  5. les js dans condcustom (et pas les vendors, on n’est pas censé les modifier)
  6. On peut attibuer la tâche watch à la tâche par défaut.

Et plus : les traductions de chaines Gettext !

La tâche translate

  1. On va voir les sources php, tous les fichiers
  2. On fait en sorte de les lire toujours dans le même ordre
  3. On leur applique le module de traduction qui va générer un .pot
  4. On envoie le résultat dans le dossier languages. On n’aura plus qu’à s’en servir pour récupérer un .mo et .po avec Poedit.

Un autre plus : du lint sur les js !

En fait, j’ai mis en place un linter js et de quoi respecter les coding standards WP (PHPCS) en global sur mon pc. Je ferai un autre tuto pour expliquer comment.

Du coup pour ma part je n’en ai pas besoin au niveau d’un projet, mais on peut le faire.

// Task 'jshint' to verify if there is errors or warning
gulp.task('jshint', function() {
  return gulp
  .src('./src/assets/js/**/*.js')
  // Verify if errors ('gulp-jshint')
  .pipe(jshint())
  // Retrieve report ('jshint-stylish')
  .pipe(jshint.reporter(stylish));
});

Autre commandes utiles

Commandes utiles pour gérer ses modules :

  • Supprimer un module : npm uninstall <package_name> ou npm rm <package_name>
  • Mettre à jour tous ses modules présents dans le dossier node_modules : npm update
  • Lister ses modules installés avec leurs dépendances : npm list ou npm ls (pour limiter la profondeur d’affichage des dépendances qui peut être long : npm ls --depth=0)
  • Avoir des infos sur un module : npm info
  • Vérifier dans ses dépendances celles qui peuvent nécessiter une mise à jour : npm outdated

Conclusion

Je ne regrette pas de m’être attelée à enfin utiliser Gulp. Même si au départ il faut prendre du temps pour comprendre comment ça fonctionne, on en gagne beaucoup ensuite une fois qu’on a acquis les bases sur le sujet. Et puis ça facilite aussi l’envoi sur le serveur de prod si en local (en dev) on a modifié des fichiers. Bref, même si j’en suis au début de sa prise en main, l’intérêt de son utilisation ne fait aucun doute !

Ce qui m’a donné le courage de m’y mettre ce sont ces différentes ressources :

Pour parfaire la compréhension du sujet vous pouvez aussi consulter :