Développement front-end en Javascript : les outils de base

Si vous démarrez de zéro, ne commencez surtout pas avec un boilerplate qui fournit tout de base, mais apprenez à comprendre et à maitriser les outils un par un. On commence par le plus basique.

NodeJS

Node est un interpréteur javascript (celui de Chrome).

$ node --version v0.11.16 $ node > 1 + 1 2

Javascript est une implémentation du standard ECMAScript. Il en existe plusieurs versions :

  • ES 5 : celui qui marche actuellement sur tous les navigateurs ;
  • ES 6 : celui qui est sorti en 2015, et qui s'appelle aussi ES2015. Pour en avoir un aperçu voir ici : es6features ;
  • ES 7 : le prochain, qui arrive en plusieurs étapes selon le processus de validation :
  •    Stage 0: Épouvantail (le plus expérimental) ;
  •    Stage 1: Proposition ;
  •    Stage 2: Brouillon ;
  •    Stage 3 : Candidat ;
  •    Stage 4 : Finalisé (l'état actuel d'ES6 depuis mi 2015).

Dans Node, les fonctions Stage 2 ou supérieures sont activées par défaut. Sur option on peut activer le Stage 1 ou encore plus expérimental, le Stage 0.

Une notion à connaître : un Polyfill est un ensemble de fonctions javascript qui comblent un manque dans un navigateur.

NVM (Node Version Manager)

NVM est un script shell pour gérer plusieurs versions de Node et de ses outils, dont NPM.

Lire https://github.com/creationix/nvm.git

La version de Node sur Debian est trop vieille : on veut donc installer une récente sans casser son système.

On installe Node 4 grâce à NVM :

$ source ~/.nvm/nvm.sh
$ nvm help
$ nvm install 4
$ nvm ls

On switche sur Node 4 (en pratique le shell est modifié pour utiliser le Node de NVM, un peu comme avec virtualenv) :

$ nvm use 4
Now using node v4.2.4 (npm v2.14.12) 
$ node --version
v4.2.4

NPM (Node Package Manager)

NPM est le gestionnaire de paquets de Node. Un peu comme pip pour Python. Avec NPM, le fichier de description du paquet s'appelle package.json :

$ npm --version
2.14.12 $
npm help

Pour installer un paquet javascript depuis le dépôt public npm, par exemple webpack :

$ npm install webpack

Ça ne fait rien d'autre que créer un sous-répertoire node_modules avec webpack dedans. S'il y a des scripts exécutables, ils atterrissent dans node_modules/.bin/

Créons notre paquet javascript :

$ mkdir tuto 
$ cd tuto 
$ npm init

Ça crée le fichier package.json. Ensuite pour ajouter une dépendance dans le paquet (par exemple webpack) :

$ npm install webpack --save

L'option --save permet de mémoriser la dépendance dans package.json.

Pour installer mon paquet avec toutes ses dépendances :

$ npm install

Donc si plus rien ne marche, ou pour recommencer de zéro, il suffit d'effacer le répertoire node_modules et relancer npm install.

Webpack

Webpack permet de transformer et d'assembler les tonnes habituelles de dépendances Javascript dans un seul fichier bundle.js pour le navigateur. En réalité c'est un outil très puissant qui peut faire beaucoup plus de choses. Ça marche aussi avec les CSS, les images, les polices, etc...

Voir : http://webpack.github.io/

Note : d'autres outils similaires à webpack existent (Browserify, Grunt, Gulp, etc.)

On installe webpack dans notre paquet (comme dépendance de dev) :

$ npm install webpack --save-dev

On crée notre appli dans un fichier javascript :

$ echo "document.body.innerHTML = 'hello world'" > index.js

On build notre projet grâce à Webpack :

$ ./node_modules/.bin/webpack index.js build/bundle.js

On peut aussi lancer cette commande avec NPM, en ajoutant dans package.json dans la partie 'scripts' :

"build": "webpack index.js build/bundle.js"

De cette façon on peut ensuite juste lancer :

$ npm run build

Au lieu de passer les options en ligne de commande, Webpack peut aussi prendre un fichier de configuration écrit en javascript.

On crée le fichier webpack.config.js :

module.exports = {
     entry: "./index",
     output: {
         path: "./build",
         filename: "bundle.js"
     }
}

Et remplacer le script de build dans package.json par :

"build": "webpack index.js --config webpack.config.js",

Webpack-dev-server

C'est un serveur http de développement qui permet de tester l'application dans le navigateur. Pour ça il faut créer un fichier index.html qui expose le js :

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8"> 
        <title>hello</title> 
    </head> 
    <body> 
        <script src="bundle.js"></script> 
    </body>
</html>

Puis on installe et on démarre :

$ npm install webpack-dev-server --save-dev
$ ./node_modules/.bin/webpack-dev-server index.js --watch

et on peut visiter http://localhost:8080, modifier le fichier index.js et recharger la page, webpack relance le build tout seul.

La même commande peut être ajoutée au package.json :

"dev": "webpack-dev-server --config webpack.config.js --watch"

De cette façon on peut lancer le serveur de dev avec :

$ npm run dev

Webpack et webpack-dev-server peuvent être configurés avec un fichier javascript. On peut créer un fichier webpack.config.js :

module.exports = {  
    entry: "./index", 
    output: {    
        path: "./build",   
        filename: "bundle.js"  
    } 
}

Et modifier les commandes dans package.json :

"build": "webpack --config webpack.config.js",
"dev": "webpack-dev-server --config webpack.config.js --watch"

Babel

Babel est un transcompilateur qui transforme par exemple du javascript ES6/ES2015 moderne en vieux javascript ES5 pour navigateurs. C'est devenu quasi-indispensable dans la stack du développeur. Ça peut être utilisé justement comme loader depuis webpack.

Voir : http://babeljs.io/

On essaie d'abord en ligne de commande de transformer du ES6 en ES5

$ npm install babel-cli babel-preset-es2015
$ echo 'add=(a,b)=>{return a+b}' | ./node_modules/.bin/babel --presets es2015

"use strict";
add = function (a, b) {
    return a + b;
};

On essaie maintenant de mettre du ES6 dans notre fichier index.js assemblé par Webpack, en remplaçant le contenu par :

let txt = 'hello world'; 
document.body.innerHTML = txt;

let est une nouvelle instruction pour déclarer des variables avec un scope limité au bloc en cours. Si on recharge la page on obtient une page blanche et une erreur dans la console du navigateur (F12 → Console):

SyntaxError: missing ; before statement

Pour pouvoir utiliser la syntaxe ES6, il faut dire à Webpack d'utiliser Babel pendant l'assemblage.

Il faut d'abord installer Babel 6 et son loader pour Webpack :

$ npm install babel-core babel-loader --save-dev

Puis lui indiquer ce loader en ajoutant ceci dans webpack.config.js :

module: {  
    loaders: [   
        { exclude: /node_modules/,   
         loader: "babel-loader",   
         query: {
             presets: ['es2015'] 
         }
        }   
    ]
}

En redémarrant npm run dev, on devrait voir de nouveau le hello world.

Grâce à d'autres loaders, on peut transformer des fichiers de tous types. Par exemple : jsx-loader, sass-loader, react-hot-loader, etc...

Au lieu de spécifier les presets dans le fichier de webpack, vous pouvez aussi les mettre dans un fichier .babelrc :

{
  "presets": ['es2015']
}

Si vous utilisez Mocha (voir ci-dessous), vous serez obligé de créer ce fichier .babelrc pour que Mocha l'utilise car il appelle directement Babel.

Mocha

Mocha est un lanceur de tests unitaires.

Voir https://mochajs.org

Il suffit d'écrire un fichier de test vérifiant des assertions, Mocha affichera de manière succinte les tests réussis et les tests échoués.

On commence par l'ajouter dans les dépendances de dev :

$ npm install mocha --save-dev

Ensuite on écrit un test unitaire dans un fichier test.js :

describe('Un regroupement de tests', function() { 
    it('Un testcase', function() { 
        assert(2+2 == 4)   
    })
});

describe() permet de regrouper plusieurs tests dans un jeu de tests, et it() permet de décrire un test en particulier. On peut mettre plusieurs it() dans un describe() et plusieurs sous-describe() dans un describe()  englobant, pour organiser ses tests. Ne cherchez pas à importer ces fonctions, c'est Mocha qui se débrouille pour le faire.

Ensuite on peut lancer les tests simplement avec :

$ ./node_modules/.bin/mocha

Si votre code contient du ES2015 et que vous voulez le convertir en ES5 avant de lancer les tests, vous pouvez ajouter une option pour utiliser Babel 6:

$ ./node_modules/.bin/mocha --compilers js:babel-core/register

Et finalement si vous voulez éviter de taper cette longue commande, ajoutez-là comme script NPM dans le package.json, sous la clé 'test', et vous pourrez alors lancer les tests avec :$ npm run test

Chai

Chai est une bibliothèque d'assertions pour les tests unitaires et le TDD (Test Driven Development), permettant de faire des choses un peu plus évoluées qu'un simple assert().

Voir http://chaijs.com.

Par exemple un assert.deepEqual() pour comparer deux objets et leurs contenus :

import {assert} from 'chai';
const foo = {a: 1, b: 2};
const bar = {...o1};
assert.deepEqual(foo, bar);

On peut aussi utiliser des syntaxes orientées BDD (Behaviour Driven Development), comme should() ou expect().  L'intérêt du BDD est qu'il autorise l'écriture de tests en pseudo langage naturel. Par exemple :

import {expect} from 'chai';
expect(stuffs).to.have.length(3);

ou bien la syntaxe should() qui doit être précédée d'une invocation :

import {should} from 'chai';
should();
stuff.should.have.property('color').with.length(3);

Finalement quand on assemble Chai et Mocha, en reprenant ce même exemple, ça donnera donc quelque chose comme :

import {should} from 'chai';
should();

describe('Mon groupe de tests', function() {
    it('should have a wanted color and length', function() {
        stuff.should.have.property('color').with.length(3)
    })
});