Découverte de JSPM, ou comment utiliser ES6 et les modules dans son projet
Ici, nous allons aborder l'usage de JSPM: http://jspm.io/
Ce dernier est outils NodeJs qui permet d'avoir une surcouche au gestionnaire de dépendance NPM afin de pouvoir télécharger et utiliser des modules ES6.
Qui plus est, il va également se reposer sur la "norme" SystemJs afin de pouvoir charger dans le navigateur les modules ES6.
Et enfin, JSPM va installer un transpileur ES6 pour compiler le code de notre projet !
Voici une petite démonstration de JSPM, qui est basé sur ce tutoriel. Nous allons juste afficher quelques super-héros.
Installation
Pour commencer, il faut installer JSPM via NodeJs. Il est conseillé de l'avoir en commande globale. Pour cela, tapez la ligne de commande suivante:
> npm install jspm -g
Plutôt simple pour l'instant.
Générer son application
Maintenant, nous devons créer le répertoire de notre projet et ouvrir un terminal pointant sur ce dernier. Une fois fait, il faudra alors taper la ligne de commande suivante:
> jspm init
Tout comme son équivalent NPM, nous allons alors avoir un nombre de questions dans notre shell qui va nous permettre de générer notre projet. Nous aurons comme questions:
- Devons-nous créer le fichier "package.json"
- le nom de répertoire du projet fera foi en tant que nom
- Devons-nous figer la version de JSPM
- Plutôt une bonne pratique, qui évite des soucis entre les versions de JSPM
- Quel est le chemin relatif de bases ?
- Pour trouver nos fichiers JavaScripts
- Quel est le nom du répertoire où JSPM va télécharger les dépendances
- Par défaut "jspm_packages"
- Bower fait la même chose: il ne télécharge pas les dépendances dans "node_modules", mais dans un répertoire tierce que seul Bower gére
- Quel est le chemin de configuration JSPM ?
- Par défaut "config.js"
- C'est dans ce fichier que nous pourrons modifier ultérieurement le chemin relatif, les modules importés, ...
- Est-ce que nous voulons utiliser un transpiler ES6 ?
- Tout dépend si vous voulez écrire votre projet en ES6 ou non
- Cela dépend également si vous utilisez NodeJS 4 (ou plus) qui gère pour du code serveur l'ES6 naturellement
- Dans le cas d'applications Web, il est recommandé d'utiliser le transpiler
- Vous pourrez choisir entre Babel, Traceur et TypeScript
- Pratique: si vous souhaitez partir sur de l'Angular 2, vous pourrez utiliser JSPM pour initialiser votre projet et configurer TypeScript
Une fois que les questions passées, JSPM va alors générer les fichiers de bases, et télécharger les modules importants, comme le transpileur, SystemJs, ...
Créer son application
Par défaut, nous allons trouver dans notre projet ce qui suit:
Nous retrouvons bien le fichier config.js et le répertoire "jspm_packages". Nous allons ajouter pour créer notre application:
- Un fichier html
- Un répertoire de styles
- Un service pour récupérer la liste des utilisateurs
- Un service pour initialiser l'application Web
Nous aurons alors la structure suivante:
Le but étant d'avoir cette (moche, mais couleur de super-héros oblige) application listant des super-héros et permettant de les rechercher:
Contenu du service "users.js" gérant les utilisateurs
/** * Module to manage users * @author Julien Roche * @module app/services/users * @exports UsersService * @version 1.0 * @since 1.0 */ 'use strict'; /** * @class User */ class User { /** * @constructor * @param {string} [uuid='1'] * @param {string} [firstName='Bruce'] * @param {string} [lastName='Wayne'] * @param {number} [age=36] */ constructor (uuid = '1', firstName = 'Bruce', lastName = 'Wayne', age = 38) { /** * @property {string} uuid */ this.uuid = uuid; /** * @property {string} firstName */ this.firstName = firstName; /** * @property {string} lastName */ this.lastName = lastName; /** * @property {number} age */ this.age = age; } /** * Return the string representation of a user * @returns {string} */ toString() { return `(${this.uuid}) ${this.firstName} ${this.lastName}: age ${this.age}`; } } /** * List of users * * @constant * @private * @type {User[]} */ const USERS = [ new User(), new User('2', 'Peter', 'Parker', 22), new User('3', 'Clark', 'Kent', 32), new User('4', 'Tony', 'Stark') ]; /** * @class UsersService */ export default class UsersService { /** * Return the list of @{User} * * @method * @static * @returns {User[]} */ static fetch() { return USERS; } /** * Find the expected user by name * * @method * @static * @param {string} name * @returns {User} */ static findByName(name) { return USERS.find((user) => user.firstName === name); } }
Contenu du service "index.js" permettant d'initialiser l'application
/** * Main module to instantiate the application * @author Julien Roche * @module app/index * @version 1.0 * @since 1.0 */ 'use strict'; console.log('Index.js is loaded'); // First imports (at the top of the files) import UsersService from './services/users'; var ulElement = document.querySelector('ul'); // Inject users into the HTML UsersService.fetch().forEach( (user) => { let liElement = document.createElement('li'); liElement.innerHTML = user.toString(); ulElement.appendChild(liElement); } ); // Listen to events document .querySelector('button') .addEventListener( 'click', (event) => { event.preventDefault(); let name = window.prompt('Please fill a name to search'); if (name) { let foundUser = UsersService.findByName(name); if (foundUser) { window.alert('Somebody savvvvveed meeeeeeeeeeeeee'); } else { window.alert(`${name}, where are you ???`); } } }, false );
Contenu du fichier index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>ES6 application with JSPM</title> <!-- Import styles --> <link rel="stylesheet" href="styles/index.css" /> <!-- Base imports --> <script type="text/javascript" charset="utf-8" src="jspm_packages/system.js"></script> <script type="text/javascript" charset="utf-8" src="config.js"></script> </head> <body> <h1>A super list</h1> <ul></ul> <hr /> <br /> <button type="button">Find by name</button> <!-- Start our application --> <script type="text/javascript" charset="utf-8"> System .import('./app/index') .then(function () { console.info('Application loaded'); }) .catch(function (ex) { console.error(ex.stack || ex); }); </script> </body> </html>
Comme nous pouvons le voir, dans ce fichier nous importons le fichier de configuration de JSPM, SystemJs, et via ce dernier, nous demandons de charger le module qui va initialiser notre application.
Bigre, ça ne marche pas
Là, nous pouvons nous apercevoir que notre application ne va pas marcher, car SystemJs ne va réussir à charger les modules:
C'est là que nous allons devoir modifier le fichier "config.js". Après, nous pourrions pu éviter cela lors de la création du projet, mais c'est juste pour montrer la logique de JSPM. En effet, quand nous ouvrons le fichier de configuration, nous pouvons voir la chose suivante:
La propriété "baseURL" est mis avec "/". Ce qui veut dire que nous devons importer nos fichiers de façon absolue. Ce qui va un peu à l'encontre à la fois de la philosophie NodeJs où nous devons importer en spécifiant un chemin relatif au module. Mais aussi un peu à ES6.
En effet, en utilisant SystemJS, nous pouvons choisir le type de loader AMD (à savoir CommonJS, RequireJS) et le configurer (définir une URL de base, des raccourcis, chargement synchrone / asynchrone ...). Or dans la norme ES6, nous n'avons pas vraiment cette notion de configuration (pour l'instant: c'est un point de débat). Du coup, nous allons alors naturellement utiliser des chemins relatifs.
Il suffit alors de changer le fichier "config.js" comme suit (en ne mettant rien dans "baseURL"):
Ouf, ça marche
Et là, nous avons enfin notre application qui fonctionne.
Ce que nous pouvons-dire
Personnellement, je trouve cela pas mal. C'est plutôt simple à utiliser. Il y a la notion de "bundle" qui est encore pour moi un peu floue (qui permet de créer son propre module packagé pour JSPM).
Après, je m'interroge: nous allons pouvoir utiliser de plus en plus NodeJs en mode ES6. Nous allons de plus en plus écrire des applications isomorphiques. Et de plus en plus, des frameworks frontend sont accessibles via NPM directement (je peux citer Lodash, jQuery, ...).
Du coup, la question que je me pose: est-ce que ce n'est pas un outils "de trop" ?
A voir par la suite.
Commentaires
Enregistrer un commentaire