Une introduction pour débutants ambitieux

Cet article a pour objet de présenter certains des outils actuellement les plus populaires pour développer des applications web modernes en JavaScript. Ces outils ne viennent pas de sortir et existent depuis plusieurs années maintenant. Cependant, on trouve de nombreux développeurs qui ne les utilisent pas ou ne les connaissent pas (ce qui pourrait être votre cas). C'est pourquoi cet article va essayer de vous donner une introduction rapide et concise pour vous permettre de démarrer.

NdT: Cet article est la traduction d'un excellent article de Juri Strumpflohner traduit et publié avec son autorisation.

Node et NPM

Node.js amène JavaScript sur le serveur et sur le poste de travail. Alors qu'initialement JavaScript était principalement utilisé comme un langage pour le navigateur, avec Node vous pouvez maintenant créer votre backend côté serveur ou même une application pour le poste de travail avec node-webkit (pour les plus fêlés d'entre vous).

Node.js® est une plate-forme bâtie sur l'interpréteur JavaScript de Chrome pour facilement construire des applications réseau rapides et capables de résister à de fortes charges. Node.js utilise un modèle de programmation événementielle à entrées/sorties non bloquantes qui le rend léger et efficient, parfait pour les applications temps-réel avec beaucoup de données qui s'exécutent via une architecture distribuée. nodejs.org

Créer un serveur web est aussi simple que les quelques lignes de code suivantes :

var http = require('http');
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello World\n');
}).listen(1337, '127.0.0.1');
console.log('Serveur accessible via http://127.0.0.1:1337/');

Pour le lancer, exécutez :

  $ node start
  Serveur accessible via http://172.0.0.1:1337/

Une des choses formidables avec node est son énorme communauté qui crée et publie ce qu'on appelle des node modules dans le dépôt NPM, le gestionnaire de paquets de Node. Actuellement il y a environ 90.000 modules et il y a eu aux alentours de 390.000 téléchargements le mois dernier.

A part servir à créer des applications côté serveur avec Node, il est également devenu la VM pour les outils de développement en JavaScript comme des compacteurs de code, des analyseurs statiques de code, etc. Grunt et Yeoman (décrits dans cet article) s'appuient tous les deux sur l'infrastructure de Node.

Davantage sur nodejs.org et npmjs.org.

Installer Node

Donc, pour démarrer, vous devez d'abord installer l'exécutable Node. Le meilleur moyen de le faire consiste à télécharger le paquet souhaité sur le site officiel. Ceci installera automatiquement NPM sur votre machine.

Une fois que c'est fait, saisir ..

  $ node -v

..dans votre terminal doit provoquer l'affichage de la version installée de node, ce qui confirme que vous êtes prêt à démarrer.

Installer des paquets node

Installer un paquet node n'est pas plus compliqué qu'exécuter :

  $ npm install grunt

Ceci installe le paquet node grunt dans un répertoire appelé node_modules.

Démo: installation d'un module node

Toutefois, créer un fichier package.json est une bien meilleure pratique. Puisque l'approche suggérée consiste à ne pas livrer le contenu de votre dossier node_modules dans votre gestionnaire de code source mais plutôt de réinstaller automatiquement les modules qu'il contient lors du processus de construction, vous avez besoin d'un endroit pour indiquer les paquets installés et les versions utilisées : package.json.

Pour créer un nouveau fichier package.json, exécutez simplement npm init dans un répertoire propre. Vous devrez répondre à quelques questions mais au final vous obtiendrez un chouette nouveau fichier de configuration des paquets.

Exemple de fichier package.json

A chaque fois que vous installerez de nouveaux paquets, vous utiliserez l'option --save ou --save-dev pour conserver le paquet dans le fichier package.json. Par exemple, exécuter ...

  $ npm install --save-dev grunt

... ajoutera automatiquement grunt à la section devDependencies (dépendances pour le développement) du fichier de configuration des paquets :

{
  ...
  "devDependencies": {
    "grunt": "^0.4.5"
  }
}

De la même façon, si vous ajoutez --save, il sera ajouté à la section dependencies. La différence principale est que les dependencies sont directement utilisées par votre application et doivent être déployées avec elle. A l'inverse, les "devDependencies" sont des outils que vous utilisez pendant le développement de l'application et n'ont normalement pas besoin d'être déployés avec elle. Ce sont par exemple des scripts compacteurs de code, des lanceurs de tests, etc.

Pour désinstaller (uninstall) un paquet, utilisez ..

  $ npm uninstall --save-dev grunt

.. qui désintalle grunt et le retire de package.json.

Restorer les paquets

Comme je le mentionnais, vous ne livrez normalement pas le répertoire node_modules à votre gestionnaire de code. Aussi, lorsque vous, en tant que développeur, ou lorsque le serveur de construction, récupérez le code source depuis le gestionnaire de source, les paquets doivent être restaurés d'une manière ou d'une autre. C'est là que le fichier package.json entre de nouveau en jeu. Ayant ce fichier dans le répertoire racine, exécuter ...

  $ npm init

.. indique à NPM de lire les dépendances dans le fichier de configuration et de les restaurer en utilisant la version indiquée.

Gestion des versions

NPM utilise la gestion sémantique des versions (Semantic Versioning) des paquets.

Etant donné un numéro de version MAJEUR.MINEUR.CORRECTIF, incrémentez la :

- version MAJEUR quand vous faîtes des modifications d'API incompatibles avec la version précédente,
- version MINEUR quand vous ajoutez des fonctionnalités en respectant la compatibilité ascendante et
- version CORRECTIF quand vous faîtes des corrections de bugs en respectant la compatibilité ascendante.

http://semver.org/

Chaque paquet présent dans package.json est listé avec sa version et la façon de gérer ses mises à jour. Vous pouvez trouver les configurations suivantes :

  • 1.3.5 : indique à npm d'utiliser exclusivement la version indiquée du paquet (la plus restrictive).
  • ~1.3.5 or 1.3.x : indique à npm de limiter les mises à jour du paquet concerné aux évolutions de versions correspondant à des correctifs (normalement seulement des corrections de bugs). NPM définit cela comme ceci ~1.3.5 := >=1.3.5-0 <1.4.0-0.
  • ^1.3.5 : indique à npm qu'il peut faire toutes les mises à jours inférieures à la prochaine version majeure : <2.0.0. Ceci est le comportement par défaut quand vous installez les paquets de node (auparavant, c'était ~). NPM définit cela comme ceci 1.3.5 := >=1.3.5-0 <2.0.0-0.
  • latest ou * : indique à npm de toujours mettre à jour avec la version la plus récente (non recommandé).

Bower

Bower est au navigateur web ce que NPM est à Node.js. C'est un gestionnaire de paquets pour vos bibliothèques de développement de la couche de présentation telles que jQuery, Bootstrap et ainsi de suite.

Vous installez Bower comme un paquet global à l'aide de NPM (évidemment)

  $ npm install -g bower

Ensuite, de façon similaire à ce que vous aviez fait avec NPM, vous exécutez bower init dans votre terminal pour créer un nouveau fichier de configuration bower.json (l'équivalent de package.json pour NPM).

Exemple de fichier bower.json

Installer des paquets se fait de la même façon qu'avec NPM.

  $ bower install --save jquery

Vous pouvez également télécharger une version spécifique en ajoutant jquery#1.9.1. Notez que l'option --save (ou -S) ajoute la dépendance à votre fichier de configuration bower.json. Les paquets installés seront placés dans le répertoire bower_components. Il est conseillé de ne pas livrer ce répertoire à votre gestionnaire de code source (juste comme pour le répertoire node_modules).

Pour désinstaller un paquet, utilisez simplement

  $ bower uninstall --save jquery

Ce qui est particulièrement intéressant est que Bower vous permet d'installer des paquets depuis n'importe que dépôt git ou même via une simple URL.

  $ bower install git:/github.com/user/package.git

ou

  $ bower install http://exemple.com/script.js

Si vous avez besoin d'une configuration plus élaborée, comme modifier le nom du répertoire des dépendances ou son emplacement, vous pouvez utiliser un fichier de configuration .bowerrc placé à la racine de la structure de répertoires de votre projet. Plus d'informations sur les options de configuration disponibles peuvent être trouvées sur le site officiel.

Il existe un autre article sympathique que j'ai trouvé sur Medium qui fournit une rapide introduction à Bower que vous pourriez également vouloir consulter.

Yeoman

Yeoman est devenu le standard de facto de la boîte à outil de génération du squelette de code pour créer des applications JavaScript modernes.

Yeoman s'appuie sur des générateurs qui sont soit développés par l'équipe officielle de Yeoman, soit par la communauté Open Source. Yeoman lui-même ne fournit de base que l'infrastructure pour construire et exécuter ces générateurs.

Yeoman vous aide à rapidement démarrer de nouveaux projets en prescrivant les meilleures pratiques et outils pour vous aider à rester productif. Extrait du site officiel

Ce qui est bien avec une telle approche est que :

  • vous pouvez rapidement démarrer. Créer la configuration d'un projet avec les outils appropriés et prise en charge du développement peut vous prendre beaucoup de temps et nécessiter une vraie expertise.
  • vous n'avez pas forcément besoin de connaître tous les meilleures pratiques en matière d'outils disponibles sur le marché. Yeoman les assemble pour vous si bien que vous pouvez démarrer immédiatement. Ensuite, une fois que vous serez plus expérimenté, vous pourrez adapter la configuration de Yeoman pour l'ajuster plus finement aux besoins de votre projet.
  • c'est une formidable façon de découvrir des tas et des tas de nouveaux outils.

Yeoman ainsi que ses générateurs sont distribués sous forme de modules node. Installez-les simplement globalement :

  $ npm install -g yo

Trouvez ensuite votre générateur (par exemple pour angular) et installez-le en utilisant la commande suivante :

  $ npm install -g generator-angular

Enfin, exécutez le générateur dans le répertoire de votre projet pour créer une nouvelle application :

  $ yo angular nom-application

Ceci va créer le squelette initial à partir duquel vous pourrez ensuite commencer à construire votre application. Mais, selon le générateur que vous utilisez,Yeoman va encore plus loin et vous permet également de générer des composants au coup par coup comme des contrôleurs Angular, des directives, etc. au fur et à mesure du développement.

  $ yo angular:controller user

C'est tout en ce qui concerne l'utilisation de Yeoman. Les sujets plus avancés concernent la création de vos propres générateurs. Il suffit d'étudier la documentation qui est plutôt détaillée.

Grunt

Grunt, c'est l'automatisation. C'est un outil de construction en ligne de commande à base de tâches pour les projets JavaScript. La description officielle : "L'exécuteur de tâches JavaScript".

Pour démarrer, suivez simplement le guide en ligne du site officiel. Il existe également un super livre Getting Started with Grunt - The JavaScript Task Runner publié par PacktPub qui est idéal pour les débutants.

Une autre bonne présentation est celle de "Cowboy" Ben Alman Bocoup concernant l'état des lieux de Grunt (Août 2014). Installation

Grunt s'exécute sur la plate-forme Node.js et est distribué via le dépôt npm. Il se présente sous la forme de deux outils distincts

  • grunt-cli qui est l'interface en ligne de commandes de Grunt
  • module grunt

La raison pour laquelle il y a deux composants est de s'assurer que vous puissiez faire cohabiter différentes versions de grunt (i.e. d'anciennes versions pour des projets plus anciens). C'est pourquoi grunt-cli est installé globalement tandis que grunt est installé projet par projet.

  $ npm install -g grunt-cli

Puis allez dans le répertoire du projet pour lequel vous souhaitez exécuter Grunt et exécutez :

  $ npm install grunt

Gruntfile.js

Gruntfile.js est l'endroit où vous configurez les tâches Grunt pour votre projet. Au début, ce fichier est aussi simple que ça :

module.exports = function(grunt) {
  // Faire des trucs de Grunt ici
};

L'objet grunt est l'API: http://gruntjs.com/api/grunt de Grunt. Il vous permet d'interagir avec Grunt, d'enregistrer vos tâches et d'ajuster leur configuration.

modules Grunt

Les modules de Grunt modules sont distribués via le dépôt NPM de Node. Normalement, ils sont préfixés par grunt- et les plugins officiels de grunt par grunt-contrib-. Exemple: grunt-contrib-uglify.

Par conséquent, les modules Grunt modules sont des modules node si bien que vous les installez simplement comme mentionné précédemment.

  $ npm install --save-dev grunt-contrib-uglify

Anatomie des tâches Grunt

Normalement, vous commencez par définir les tâches de construction comme cet exemple d'une tâche stringCheck extrait du livre Grunt mentionné plus haut.

module.exports = function(grunt){
  ...
  grunt.initConfig({
    stringCheck: {
      file: './src/un_fichier.js',
      string: 'console.log('
    }
  });
}

Comme vous pouvez le voir, une tâche est simplement une fonction que vous enregistrez avec Grunt.

module.exports = function(grunt){
  grunt.registerTask('stringCheck', function() {
    //échoue si la configuration n'est pas fournie
    grunt.config.requires('stringCheck.file');
    grunt.config.requires('stringCheck.string');

    //récupère le nom du fichier et ouvre ce fichier
    var file = grunt.config('stringCheck.file');
    var contents = grunt.file.read(file);

    //récupère la chaîne à chercher
    var string = grunt.config('stringCheck.string');

    if(contents.indexOf(string >= 0))
      grunt.fail.warn('"' + string + '" trouvé dans "' + file + '"');
    });
}

Notez que les tâches téléchargées ailleurs via NPM doivent être chargées d'abord pour pouvoir être utilisées dans votre Gruntfile.js. Ceci est réalisé en utilisant la méthode loadNpmTasks de l'objet grunt.

module.exports = function(grunt){
  grunt.loadNpmTasks('grunt-contrib-concat');
  ...
}

Pour ne pas avoir à faire ça pour chacune des tâches que vous utilisez (et il peut y en avoir pas mal), vous pourriez vouloir utiliser le plugin load-grunt-tasks et exécuter require('load-grunt-tasks')(grunt) au début de votre Gruntfile.js. Ceci va charger automatiquement tous les modules grunt qui seront prêts à être utilisés.

Multitâche

Grunt vous permet également de grouper l'exécution des tâches comme suit :

module.exports = function(grunt){
  ...
  grunt.initConfig({
    stringCheck: {
      target1: {
        file: './src/unfichier.js',
        string: 'console.log('
      },
      target2: {
        file: './src/unfichier.js',
        string: 'eval('
      }      
    }
  });
}

Vous pouvez alors les exécuter avec grunt stringCheck:target1 et grunt stringCheck:target2. target1 et target2 peuvent (et doivent) évidemment être nommées différemment.

Jokers

Les jokers sont une façon de capturer un vaste ensemble de fichiers avec une seule expression plutôt que de les lister tous un par un, ce qui souvent même impossible. Tiré de la documentation officielle:

  • * correspond à tout nombre de caractères mais pas à /
  • ? correspond à un unique caractère mais pas à /
  • ** correspond à tout nombre de caractères, / y compris, tant que c'est un seul élément dans une partie de chemin d'accès
  • {} autorise une liste d'expressions "or" séparées par des virgules
  • @@!àà au début d'un motif inverse la correspondance

Tout ce que la plupart des gens a besoin de savoir est que foo/*.js correspondra à tous les fichiers se finissant par .js dans le sous-répertoire foo/, tandis que foo/**/*.js correspondra avec tous les fichiers se finissant par .js dans le sous-répertoire foo/ et tous ses sous-répertoires.

Puisque la plupart des tâches interagissent en fin de compte avec le système de fichier, Grunt met à disposition une structure pour faciliter la vie des développeurs. Si une expression avec jokers est indiquée, Grunt essaie de la faire correspondre avec le système de fichiers et place toutes les correspondances dans le tableau this.files de votre fonction de tâche Grunt. C'est pourquoi vous verrez beaucoup de tâches ayant la syntaxe suivante :

target1: {
  src: ['src/a.js', 'src/b.js']
}

ou

target1: {
  src: `src/{a,b}.js`,
  dest: `dest/ab.js`
}

Il est également possible de définir plusieurs ensembles de sources associés avec une destination. Dans ce but, le tableau files est utilisé.

target1: {
  files: [
    { src: 'src/{a,b,c}.js', dest: 'dest/abc.js' },
    { src: 'src/{x,y,z}.js', dest: 'dest/xyz.js' }
  ]
}

La notation objet suivante, plus compacte, est équivalente :

target1: {
  files: {
    'dest/abc.js': 'src/{a,b,c}.js',
    'dest/xyz.js': 'src/{x,y,z}.js'
  }
}

Une autre tâche courante consiste à copier un ensemble de fichiers dans un répertoire donné (par exemple avec des pré-processeurs comme SASS ou des compilateurs CoffeeScript). Au lieu de fournir des instructions particulières src et dest, nous pouvons utiliser la syntaxe suivante :

target2: {
  files: [
    {
      expand: true,
      cwd: 'lib/',
      src: '**/*.js',
      dest: 'build/',
      ext: '.min.js'
    }, 
  ],
}

La propriété expand indique à Grunt de générer la destination associée pour chaque fichier satisfaisant la correspondance. cwd indique le répertoire de travail actuel, src et dest se comprennent d'eux-mêmes et ext est l'extension à utiliser pour les fichiers de destination. Plus d'options existent et peuvent être trouvées dans la documentation officielle.

Exécuter des tâches

Votre but ultime est d'exécuter les tâches Grunt que vous avez définies. Si vous vous rappelez, vous avez précédemment installé globalement l'outil grunt-cli que vous pouvez maintenant utiliser pour exécuter une tâche.

  $ grunt task1 task2

Si vous avez une tâche à cibles multiples, utilisez alors : pour l'indiquer.

  $ grunt task:target1

Si vous exécutez $ grunt à la place, la tâche par défaut sera exécutée. Vous pouvez la configurer comme ceci :

module.exports = function(grunt) {
  grunt.registerTask('build', function() {
    console.log('building...');
  });

  grunt.registerTask('test', function() {
    console.log('testing...');
  });

  grunt.registerTask('default', ['build', 'test']);
};

Cette configuration de Gruntfile.js exécute build et test quand vous saisissez grunt dans votre console.

Gulp

Cette intro ne serait pas complète si elle ne mentionnait pas Gulp. Gulp est le nouveau venu dans les exécuteurs de tâches JavaScript construits sur Node.js streams. Il vise à faciliter la construction de scripts en utilisant le paradigme de "préférer coder que configurer" (à l'inverse de Grunt qui s'appuie sur la configuration).

l'utilisation que fait gulp de streams et du code-plutôt-que-configuration conduit à un build plus simple et plus intuitif. gulpjs.com

L'auteur n'a pas étudié Gulp en détail pour le moment mais vous devriez vraiment garder un oeil sur ce dernier car ce projet évolue et gagne rapidement en notoriété. Pour le moment, il ne sera pas ajouté plus de détails mais cet article sera certainement mis à jour dès que l'auteur se sera penché plus en détail sur Gulp.