ES6 en détails est une série d’articles décrivant les nouvelles fonctionnalités ajoutées au langage de programmation JavaScript avec la sixième édition du standard ECMAScript (ES6 en abrégé).

ES6 est bien là, certains parlent déjà d’ES7, de ce que le futur nous réserve comme fonctionnalités brillantes et standardisées. En tant que développeurs web, nous nous demandons comment nous pouvons exploiter tout ça. À plusieurs reprises, dans les billets précédents de cette série, je vous ai encouragés à développer dès aujourd’hui en utilisant ES6 avec quelques outils intéressants. Je me souviens notamment avoir écrit :

Si vous souhaitez utiliser cette fonctionnalité, vous pouvez utiliser Babel ou Traceur, de Google, pour traduire votre code ES6 en du code ES5, utilisable dès maintenant sur le Web.

Aujourd’hui, nous allons vous montrer comment utiliser ces outils, étape par étape. Ceux-ci sont appelés des transpileurs (transpilers en anglais). On les appelle parfois des compilateurs source à source, c’est-à-dire un compilatur qui traduit un programme d’un certain langage vers un autre langage pourvu que ces langages aient des niveaux d’abstraction comparables. Les transpileurs nous permettent d’écrire du code ES6 qui pourra être exécuté sans problème dans chaque navigateur.

Faire du vieux avec du neuf

Un transpileur est très simple à utiliser. Il s’explique en seulement deux étapes :

1. On écrit du code avec la syntaxe ES6

let q = 99;
let maVariable = `${q} bouteilles de lait, ce qui fait ${q} x 0.50€`;

2. On utilise le code qu’on vient d’écrire comme entrée pour le transpileur qui le traitera et produira le code suivant en sortie :

"use strict";

var q = 99;
var maVariable = "" + q + " bouteilles de lait, ce qui fait " + q + " x 0.50€"

Et ce code là, c’est du bon vieux JavaScript qui peut être utilisé dans n’importe quel navigateur.

Le fonctionnement interne d’un transpileur est très complexe et ce n’est pas l’objet de cet article que d’expliquer en détails comment il fonctionne. De la même façon qu’on peut conduire une voiture sans savoir comment les pièces d’un moteur sont imbriquées, aujourd’hui, nous considèrerons simplement le transpileur comme une boîte noire qui peut traiter notre code.

Babel, à l’attaque !

Il existe différentes façons d’utiliser Babel dans un projet. Il y a l’outil en ligne de commande qui peut être utilisé avec des commandes comme :

babel script.js --out-file script-compiled.js

Il y a aussi une version pour les navigateurs qui est disponible : vous pouvez inclure Babel comme n’importe quelle autre bibliothèque JS. Cela fonctionne en plaçant votre code ES6 dans des éléments script dont le type est "text/babel".

<script src="node_modules/babel-core/browser.js"></script>
<script type="text/babel">
// Votre code ES6
</script>

Ces méthodes ne conviennent plus lorsque la base de code grandit et que le code se retrouve partagé entre plusieurs fichiers dans différents dossiers. À un moment, vous devrez utiliser un outil de build (NdT : on ne parle pas ici d’un système de compilation qui produirait un code machine de bas niveau) et d’une méthode pour intégrer Babel dans cet outil et les étapes qui le composent.

Dans les sections qui suivent, nous intègrerons Babel avec Broccoli.js qui est un outil de build. Nous utiliserons ensuite plusieurs exemples et quelques lignes d’ES6 pour démarrer. Si vous rencontrez un problème, vous pourrez réutiliser le code source présent dans ce dépôt : broccoli-babel-examples. Au sein de ce dépôt, vous trouverez trois projets utilisés pour les exemples :

  1. es6-fruits
  2. es6-website
  3. es6-modules

Chacun de ces exemples est construit à partir du précédent. Nous commencerons avec le strict minimum et nous progresserons vers une solution générique pouvant être utilisée comme point de départ pour un projet ambitieux. Dans ce billet, nous aborderons les deux premiers exemples en détails. Une fois que nous aurons fini avec eux, vous serez en mesure de lire et de comprendre le code du troisième exemple.

Si vous vous dites : « je vais attendre que les navigateurs supportent ces nouvelles fonctionnalités, ce sera plus simple », vous vous retrouverez derrière les autres. Une conformité totale prend du temps pour s’installer dans les différents navigateurs, si elle arrive un jour. Les transpileurs font partie du paysage et vont y rester, il est prévu d’avoir des standards ECMAScript chaque année. On aura donc de nouveaux standards publiés plus souvent qu’on aura des navigateurs homogènes. Montez dans le train et tirez parti des nouvelles fonctionnalités dès maintenant.

Notre premier projet avec Broccoli et Babel

Broccoli est un outil conçu pour construire des projets le plus rapidement possible. Entre autres choses, vous pouvez minifier et compresser vos fichiers grâce à des extensions Broccoli. Cela vous évite d’avoir à gérer des fichiers, répertoires et d’avoir à exécuter des commandes à chaque fois que vous apportez une modification à un projet. Il faut voir Broccoli comme :

un outil comparable aux outils Rails qui fonctionnent par étapes (pipeline) mais qui fonctionne avec Node et qui est agnostique par rapport au serveur utilisé.

Mise en place du projet

Node

Comme vous avez pu le deviner, vous aurez besoin d’installer Node 0.11 ou une version ultérieure.

Si vous utilisez un système de type Unix, évitez d’installer node depuis le gestionnaire de paquets (apt,yum, etc.). Cela vous évitera d’avoir à utiliser les privilèges root lors de l’installation. Il est préférable d’installer les binaires manuellement grâce au lien précédent, avec votre utilisateur courant. Pour mieux comprendre pourquoi il n’est pas recommandé d’utiliser root et node, vous pouvez lire Do not sudo npm (en anglais). Dans cet article, vous trouverez également d’autres méthodes alternatives pour l’installation.

Broccoli

Pour mettre en place notre projet Broccoli, commençons par :

mkdir es6-fruits
cd es6-fruits
npm init
# On crée un fichier vide nommé Brocfile.js
touch Brocfile.js

Ensuite, on installe broccoli et broccoli-cli

# la bibliothèque broccoli
npm install --save-dev broccoli
# l'outil pour la ligne de commande
npm install -g broccoli-cli

Écrivons un peu d’ES6

Tout d’abord, on crée un dossier src et on y place un fichier fruit.js.

mkdir src
vim src/fruits.js

Dans ce fichier, on écrit un petit script qui utilise une syntaxe ES6.

let fruits = [
  {id: 100, nom: 'fraise'},
  {id: 101, nom: 'raison'},
  {id: 102, nom: 'pêche'}
];

for (let fruit of fruits) {
  let message = `ID : ${fruit.id} nom : ${fruit.nom}`;

  console.log(message);
}

console.log(`Total de la liste : ${fruits.length}`);

Ce fragment de code ci-dessus utilise trois fonctionnalités ES6 :

  1. let pour utiliser des déclarations limitées à la portée (ça fera l’objet d’un prochain billet)
  2. une boucle for-of
  3. des gabarits de chaîne de caractères

Maintenant, sauvegardez le fichier et essayez de le lancer

node src/fruits.js

Ça ne fonctionne pas encore tout à fait mais nous allons le rendre exécutable pour Node ainsi que pour n’importe quel navigateur.

let fruits = [
    ^^^^^^
SyntaxError: Unexpected identifier

Un peu d’efforts, on va transpiler

Nous allons utiliser Broccoli pour charger notre code et le faire passer par Babel. Pour cela, nous éditons le fichier Brocfils.js et nous ajoutons ce code :

// on importe l'extension babel
var babel = require('broccoli-babel-transpiler');

// on récupère la source et on la transpile en une étape
fruits = babel('src'); // src/*.js

module.exports = fruits;

Remarquons qu’il faut ici broccoli-babel-transpiler qui est un plugin Babel fonctionnant avec la bibliothèque Babel. Il faut l’installer avec :

npm install --save-dev broccoli-babel-transpiler

Nous pouvons ensuite construire notre projet et exécuter notre script avec :

broccoli build dist # on compile
node dist/fruits.js # on exécute le code ES5

Ce qui devrait produire quelque chose comme :

ID : 100 nom: fraise
ID : 101 nom: raison
ID : 102 nom: pêche
Totale de la liste : 3

Et voilà, aussi simple que ça ! Vous pouvez ouvrir le fichier dist/fruits.js pour voir le code transpilé. Une des fonctionnalités appréciables du transpileur Babel est qu’il produit toujours un code lisible.

Écrivons du code ES6 pour un site web

Dans notre second exemple, nous allons monter d’une niveau. Tout d’abord, quittez le dossier es6-fruits et créez un nouveau répertoire es6-website avec les étapes que nous avons vues au paragraphe Mise en place du projet ci-avant.

Dans le dossier src, nous créerons trois fichiers :

  • src/index.html

    <!DOCTYPE html>
    <html>
      <head>
        <title>Aujourd'hui : ES6</title>
      </head>
      <style>
        body {
          border: 2px solid #9a9a9a;
          border-radius: 10px;
          padding: 6px;
          font-family: monospace;
          text-align: center;
        }
        .color {
          padding: 1rem;
          color: #fff;
        }
      </style>
      <body>
        <h1>Aujourd'hui : ES6</h1>
        <div id="info"></div>
        <hr>
        <div id="content"></div>
    
        <script src="//code.jquery.com/jquery-2.1.4.min.js"></script>
        <script src="js/mon-app.js"></script>
      </body>
    </html>
    

  • src/print-info.js

    function printInfo() {
      $('#info')
      .append('<p>Un exemple de site web minimaliste avec ' +
              'Broccoli et Babel</p>');
    }
    
    $(printInfo);
    

  • src/print-colors.js

    // Un générateur ES6
    function* hexRange(start, stop, step) {
      for (var i = start; i < stop; i += step) {
        yield i;
      }
    }
    
    function printColors() {
      var content$ = $('#content');
    
      // un exemple inventé de toutes pièces
      for ( var hex of hexRange(900, 999, 10) ) {
        var newDiv = $('
    ') .attr('class', 'color') .css({ 'background-color': `#${hex}` }) .append(`hex code: #${hex}`); content$.append(newDiv); } } $(printColors);

Peut-être avez-vous remarqué ce « function* hexRange » ? Oui, c’est bien un générateur ES6. À l’heure actuelle, cette fonctionnalité n’est pas supportée par tous les navigateurs. Afin de pouvoir l’utiliser, nous aurons besoin d’utiliser une prothèse (ou polyfill). Ce sera Babel qui la fournira et nous pourrons la mettre en œuvre très rapidement.

La prochaine étape consiste à fusionner ces différents fichiers JS et à les utiliser dans un site web. La partie la plus compliquée sera d’écrire notre fichier Brocfile. Pour cet exemple, nous installerons 4 plugins :

npm install --save-dev broccoli-babel-transpiler

npm install --save-dev broccoli-funnel

npm install --save-dev broccoli-concat

npm install --save-dev broccoli-merge-trees

Utilisons ces plugins et modifions notre fichier Brocfile :

// Le transpileur Babel
var babel = require('broccoli-babel-transpiler');
// filtrer les arborescences (un sous-ensemble de fichiers)
var funnel = require('broccoli-funnel');
// concaténer des arborescences
var concat = require('broccoli-concat');
// fusionner des arborescences
var mergeTrees = require('broccoli-merge-trees');

// On transpile les fichiers source
var appJs = babel('src');

// On récupère la prothèse fournie par la bibliothèque
var babelPath = require.resolve('broccoli-babel-transpiler');
babelPath = babelPath.replace(/\/index.js$/, '');
babelPath += '/node_modules/babel-core';
var browserPolyfill = funnel(babelPath, {
  files: ['browser-polyfill.js']
});

// On ajoute la prothèse Babel à l'arborescence des fichiers
// transpilés 
appJs = mergeTrees([browserPolyfill, appJs]);

// On concatène les fichiers JS en un seul fichier
appJs = concat(appJs, {
  // on définit l'ordre de concaténation
  inputFiles: ['browser-polyfill.js', '**/*.js'],
  outputFile: '/js/mon-app.js'
});

// On récupère le fichier index
var index = funnel('src', {files: ['index.html']});

// On récupère toutes les arborescences pour les
// exporter et crée une seule arborescence finale
module.exports = mergeTrees([index, appJs]);

C’est le moment de construire et d’exécuter notre code :

broccoli build dist

Cette fois, vous devriez voir la structure suivante sous le dossier dist :

$> tree dist/
dist/
├── index.html
└── js
    └── mon-app.js

Et voici un site web statique qu’on peut servir avec n’importe quel serveur pour vérifier que le code fonctionne. Par exemple :

cd dist
python -m SimpleHTTPServer
# visitez http://localhost:8000

Vous devriez voir quelque chose qui ressemble à : screenshot.png

La suite avec Babel et Broccoli

Ce deuxième exemple vous montre ce qu’il est possible de faire avec Babel. Il pourra éventuellement vous suffire pour quelques projets. Si vous voulez aller plus loin avec ES6, Babel et Broccoli, n’hésitez pas à forker ce dépôt : broccoli-babel-boilerplate. C’est aussi un exemple utilisant Broccoli et Babel. Ce depôt vous permettra de manipuler les modules, les imports et les tests unitaires.

Vous pouvez essayer un exemple de cette configuration ici : es6-modules. Toute la magie de l’exemple se trouve dans le fichier Brocfile et vous verrez que ça ressemble beaucoup à ce qu’on a fait.


Comme vous pouvez le voir, Babel et Broccoli permettent d’utiliser les fonctionnalités d’ES6 dès aujourd’hui de façon très pratique. Merci à Gastón I. Silva pour le billet de cette semaine !

La semaine prochaine, ce sera le début d’une pause de deux semaines pour la série ES6 en détails (NdT : le prochain article sur tech.mozfr.org arrive la semaine prochaine :)). Nous avons déjà exploré de nombreuses nouveautés mais les fonctionnalités les plus puissantes d’ES6 restent à découvrir… Rendez-vous dans deux semaines pour la suite et un nouveau billet.