Comment créer et publier un module npm compatible navigateur avec webpack 2

L'écosystème JavaScript va tellement vite qu'il est assez difficile d'être toujours au courant de toutes les dernières technologies. Par exemple, il y a 3 ans, j'avais déjà mis un moment pour bien comprendre comment utiliser Grunt, tout ça pour réaliser très vite ensuite que le nouvel outil à la mode était Gulp. Et puis une fois encore, dès Gulp maîtrisé, tout le monde ne parlait plus que de webpack.

Pour le coup, je n'ai pas pris le train webpack dès le début puisque je me sentais vraiment bien avec Gulp. Mais la communauté grandissant et depuis le lancement de la version 2 début février 2017, je me suis dit que le moment était venu de s'y pencher.

Et puis par la même occasion, ça faisait longtemps que je voulais comprendre comment créer des modules npm propres, avec une version compatible navigateur et des scripts de test.

1. L'idée

Pour cette exemple, notre module ne va pas faire grand chose. On va créer une fonction qui va nous retourner la somme des deux arguments entrés. On refait l'addition en gros.

2. La structure

Commencez par créer un dossier que vous pouvez appeler add-function par exemple. Puis à l'intérieur ajouter les différents fichiers et dossiers suivants.

.
├── dist
├── lib
│   └── add.js
├── .babelrc
├── package.json
└── webpack.config.js

Le dossier dist sera la destination de notre module une fois compilé. Le dossier lib est là où nous allons écrire notre code source. Certains nomment ce dossier src. Le .babelrc contiendra la configuration de Babel. Babel va nous permettre de générer du code ES5 à partir de notre code source ES6. Le package.json est la configuration de notre module et enfin le webpack.config.js la configuration de webpack.

Pour générer un package.json par défault, n'hésitez pas à utiliser la commande npm init -f. Puis afin d'installer toutes les dépendances de notre projet faites :

npm install --save-dev babel-core babel-loader babel-preset-env babel-preset-es2015 jest webpack  

3. Le code

Rien de bien intéressant ici, le code est très basique. Notre fichier lib/add.jsva avoir le contenu suivant :

// lib/add.js
const add = (a, b) => {  
  return a + b;
};

module.exports = add;

4. Toujours mettre en place des tests

Mettre en place des tests est important, j'ai l'habitude d'utiliser Mocha mais Jest semble être de plus en plus à la mode, du coup pourquoi pas l'essayer.

Il suffit d'ajouter un fichier add.test.js au même niveau que notre add.js

const add = require('./add');

test('should return 10 when value (3, 7)', () => {  
  expect(add(3, 7)).toBe(10);
});

test('should return 25 when value (20, 5)', () => {  
  expect(add(20, 5)).toBe(25);
});

Dans notre fichier package.json, on indique que le script npm test doit appeler Jest

{
  "name": "add",
  "version": "1.0.0",
  "description": "Just a add function",
  "main": "lib/add.js",
  "scripts": {
    "test": "jest",
    "build": "webpack"
  },
  "license": "MIT",
  "devDependencies": {
    "babel-core": "^6.23.1",
    "babel-loader": "^6.4.0",
    "babel-preset-env": "^1.2.1",
    "babel-preset-es2015": "^6.22.0",
    "jest": "^19.0.2",
    "webpack": "^2.2.1"
  }
}

On peut maintenant faire un npm run test et voir que tout fonctionne.

5. Webpack et générer notre module pour le navigateur

Notre module fonctionne très bien pour Node.js puisque comme nous l'avons vu on peut l'appeler et utiliser la fonction add dans Jest. Si nous publions notre module tel quel sur npm les gens pourront venir l'utiliser sans problème tant qu'il utilise une version de Node.js qui prend en compte ES6.

Maintenant on veut dire à Webpack de générer une version pour navigateur. Il va donc falloir éditer le fichier webpack.config.js afin qu'il nous génère notre module.

const path = require('path');  
const webpack = require('webpack');

module.exports = {  
  entry: './lib/add.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'add.min.js',
    libraryTarget: 'umd',
    library: 'add'
  },
  module: {
    rules: [
      {
        test: /\.(js)$/,
        use: 'babel-loader'
      }
    ]
  },
  plugins: [
    new webpack.optimize.UglifyJsPlugin()
  ]
}

Les 4 propriétés importantes sont :

  • entry qui correspond à notre fichier source. Celui que l'on veut manipuler.

  • output ont défini notre fichier de sortie. Où est qu'on veut le placer. On dit aussi à Webpack que l'on veut qu'il est la Universal Module Definition.

  • module on dit à webpack que nos fichiers js doivent être compilé par Babel afin de sortir du code ES5.

  • plugins on utilise l'obfuscateur de code de webpack afin d'obtenir une version le plus léger possible.

Une dernière choses à faire dans le fichier .babelrc nous indiquons à Babel que notre code est du ES6.

{
  "presets": ["es2015"]
}

On peut maintenant générer notre version navigateur en faisant un npm run build que nous avons indiqué dans notre package.json comme script pour appeler webpack.

.
├── dist
│   └── add.min.js
├── lib
│   ├── add.js
│   └── add.test.js
├── .babelrc
├── package.json
└── webpack.config.js

Notre module est au complet. Pour tester notre fichier de sortie. On se place dans le dossier distet on ajoute un fichier index.html avec le code suivant

<!DOCTYPE html>  
<html>  
  <head>
    <meta charset="utf-8">
    <title></title>
    <script src="/add.min.js" charset="utf-8"></script>
  </head>
  <body>
    10 + 3 = <span id="result"></span>
  </body>
  <script type="text/javascript">
    document.getElementById('result').innerHTML = add(10, 3);
  </script>
</html>  

Pour voir le résultat on lance la commande

python -m SimpleHTTPServer

Et on peut voir à l'adresse http://localhost:8000 que tout fonctionne bien.

Ca y est vous pouvez maintenant publier votre module sur npm et l'utiliser partout aussi bien sur votre serveur que votre navigateur.

Vous trouverez toutes les sources de cet article ici : https://github.com/codeKonami/npm-module-starter

Thomas Foricher

Lire plus d'articles de cet auteur.