Commentaires
Les loaders sont très utilisés dans Webpack. Ils permettent de réaliser des transformations sur des fichiers utilisés dans notre application ou importés dans nos scripts, de telle sorte que leur contenu soit correctement compris et optimisé. En effet, de base Webpack ne comprend que le javascript. Il faut donc rajouter des loaders pour qu'il comprenne... tout le reste. Comme le CSS, SASS, Vue....

Les loaders se mettent dans un objet nommé "module" de notre configuration. Étant donné que les loaders ne s'appliquent pas à tous les fichiers, il faut préciser des règles qui indiquent à Webpack : tu utiliseras ce loader pour tels fichiers, celui-là pour tels autres fichiers...

Nous allons tester les loaders en en utilisant un très connu : Babel. Babel est un transpileur. Il permet de modifier du code ES6 et ESNEXT en code ES5 qui est mieux compris par les navigateurs.

Arthur, l'apprenti développeurSuper !

Notre premier loader : babel


D'abord, il nous faut installer les dépendances de Babel.
{"language":"shell","content":"npm install --save-dev babel-loader @babel/core","filename":""}


Ensuite, on va écrire notre loader dans l'objet nommé "module". Chaque loader aura la forme suivante :
{"language":"text/javascript","content":"module: {\n \trules: [\n \t\t{\n \t\ttest: , // Indique les fichiers sur lesquels utiliser le loader\n \t\texclude: , // Optionnel. Indique les fichiers à exclure de notre test\n \t\tloader: // Nom du loader\n \t\t}\n \t]\n \t}","filename":""}

Dans test, on va donc indiquer les fichiers sur lesquels appliquer notre loader. Pour ça, nous utiliserons les expressions régulières. Tout porte sur l'extension du fichier. Ainsi, la regex portera également dessus. Voici une liste non exhaustive de règles :

  • Pour les fichiers js : /\.js$/

  • Pour les fichiers css : /\.css$/

  • Pour les fichiers vue : /\.vue$/

  • Pour les fichiers sass : /\.scss$/


Ensuite, on a le paramètre exclude. En effet, parmi les fichiers ciblés par notre règle dans "test", on peut vouloir en exclure certains. Par exemple, il serait bon d'enlever tous les fichiers compris dans /node_modules/ si on cible les fichiers .js...
Et enfin, on précise le nom du loader. Pour Babel, on aura donc :
{"language":"text/javascript","content":"const path = require('path')\nmodule.exports = {\n\tmode: 'development',\n \tentry: {\n \t\tapp:'./src/app.js', \n \t\tproducts: './src/products.js'\n \t},\n \toutput: {\n \t\tfilename:'[name].bundle.js',\n \tpath: path.resolve(__dirname, 'public')\n \t},\n \tmodule: {\n \trules: [\n \t\t{\n \t\ttest: /\\.js$/,\n \t\texclude: /node_modules/,\n \t\tloader:\"babel-loader\"\n \t\t}\n \t]\n \t}\n}","filename":"webpack.config.js"}

Arthur, l'apprenti développeurDonc là si on lance webpack, notre code ES6 sera transformé en ES5 ?
Je te laisse essayer. Bilan ?

Arthur, l'apprenti développeurHé bien, il ne se passe rien de spécial...
En effet. Babel est un peu compliqué. Il faut, en plus de l'installer et de le renseigner dans Webpack, installer un preset. C'est à dire les règles qui transformations qui seront appliquées sur ES6 pour aller vers ES5. Du coup, on est reparti pour un tour :
{"language":"shell","content":"npm install @babel/preset-env --save-dev","filename":""}


Et après ça, comme souvent dans les loaders, il faut renseigner un fichier de configuration propre. Dans notre cas, il s'appelle babel.config.json. On met ce fichier à la racine de notre dossier.
{"language":"application/json","content":"{\n \"presets\": [\"@babel/preset-env\"]\n}","filename":"babel.config.json"}


Et maintenant, si on essaie avec le code suivant :

{"language":"text/javascript","content":"let a = \"bonjour\"\nconsole.log(a)","filename":"app.js"}

En sortie on a :
{"language":"text/javascript","content":"/******/ (() => { // webpackBootstrap\n/*!********************!*\\\n !*** ./src/app.js ***!\n \\********************/\neval(\"var a = \\\"bonjour\\\";\\nconsole.log(a);\\n\\n//# sourceURL=webpack://tuto-webpack/./src/app.js?\");\n/******/ })()\n;","filename":"app.js"}

Notre let a = ... s'est bien transformé en var a = ...

Arthur, l'apprenti développeurMais c'est quoi tout ce qu'il y a autour ? Et eval() ?
Tout ça c'est parce qu'on est en mode development. Si tu mets le mode production, tout ça disparait ;). On verra dans le dernier chapitre comment agir sur la façon dont Webpack génère les fichiers bundlés pour avoir, par exemple, des fichiers en mode développement plus lisibles.


Avec notre configuration de Webpack et de Babel, les codes de ce type ne passeront pas :

{"language":"text/javascript","content":"export default class Tutorial {\n\tstatic amount = 0;\n\n\tconstructor(name) {\n\t\tthis.name = name\n\t\tTutorial.amount++\n\t}\n\n\t...\n}","filename":""}

Nous allons devoir rajouter un plugin dans un premier temps (voir partie d'après pour comprendre les plugins).

{"language":"shell","content":"npm install --save-dev @babel/plugin-proposal-class-properties","filename":""}

Ensuite, il faut que Babel génère des polyfill : des bouts de code que nous souhaitons mais que le navigateur ne comprend pas et qu'on doit totalement réécrire pour que leur comportement soit compris nativement.
Pour ça, il nous faut installer également core-js 3 :

{"language":"shell","content":"npm install --save core-js@3","filename":""}

Enfin, nous devons modifier notre fichier de configuration babel pour ajouter ce plugin et indiquer la génération des polyfills :

{"language":"application/json","content":"{\n\t\"plugins\": [\n\t\t[\"@babel/plugin-proposal-class-properties\", { \"loose\" : true }]\n\t],\n\t\"presets\": [\n\t\t[\"@babel/preset-env\", {\n\t\t\t\"useBuiltIns\": \"usage\",\n\t\t\t\"corejs\":3\n\t\t}]\n\t]\n}","filename":"babel.config.json"}

Super, maintenant il faut importer les polyfill générés directement dans notre point d'entrée et une seule fois dans notre application. Dans app.js on va donc rajouter au tout début :

{"language":"text/javascript","content":"import \"core-js/stable\";\nimport \"regenerator-runtime/runtime\";","filename":"app.js"}

Et voilà, on peut maintenant npm run build et notre code fonctionnera sans soucis !


Il faut savoir qu'il y a également une autre syntaxe pour utiliser un loader qui nous évite d'écrire un fichier de configuration supplémentaire. Dans le cas de babel, tu pourras écrire webpack.config.js comme suit sans écrire de fichier babel.config.json :
{"language":"text/javascript","content":"const path = require('path')\nmodule.exports = {\n\tmode: 'development',\n \tentry: {\n \t\tapp:'./src/app.js', \n \t},\n \toutput: {\n \t\tfilename:'[name].bundle.js',\n \tpath: path.resolve(__dirname, 'public')\n \t},\n \tmodule: {\n \trules: [\n \t\t{\n \t\ttest: /\\.js$/,\n \t\texclude: /node_modules/,\n \t\tuse: {\n \t\t\tloader: \"babel-loader\",\n \t\t\toptions: {\n \t\t\tpresets: ['@babel/preset-env']\n \t\t\t}\n \t\t}\n \t\t}\n \t]\n \t}\n}","filename":"webpack.config.js"}

Arthur, l'apprenti développeurEn effet, ça marche toujours ! Mais au fait, comme tu sais tout ça ? Je veux dire, comment je trouve les loaders et si'l faut un fichier de configuration ?
En général il faut regarder la documentation de ce que tu cherches. Par exemple taper "SASS webpack" dans ton moteur de recherches. Et tu as la solution qui s'offre à toi !

Arthur, l'apprenti développeurD'ailleurs, tu pourrais me montrer pour SASS ?
Un loader pour SASS permettra à Webpack de lire les fichiers de type .scss ou .sass qui sont importés dans des fichiers JS.
Hé bien, en recherchant sur un moteur de recherches, j'obtiens le lien suivant https://webpack.js.org/loaders/sass-loader/.
D'après le guide, je dois exécuter la commande suivante :

{"language":"shell","content":"npm install sass-loader sass webpack --save-dev","filename":""}

Il est également recommandé d'installer 2 autres loaders : css-loader, qui permet de comprendre dans un fichier css les url() ou import() et style-loader, qui injecte le CSS dans le DOM.
Du coup, c'est parti :

{"language":"shell","content":"npm install --save-dev style-loader css-loader","filename":""}

Et je modifie webpack config selon la doc :
{"language":"text/javascript","content":"const path = require('path')\nmodule.exports = {\n\tmode: 'development',\n \tentry: {\n \t\tapp:'./src/app.js', \n \t},\n \toutput: {\n \t\tfilename:'[name].bundle.js',\n \tpath: path.resolve(__dirname, 'public')\n \t},\n \tmodule: {\n \trules: [\n \t\t{\n \t\ttest: /\\.js$/,\n \t\texclude: /node_modules/,\n \t\tuse: {\n \t\t\tloader: \"babel-loader\",\n \t\t\toptions: {\n \t\t\tpresets: ['@babel/preset-env']\n \t\t\t}\n \t\t}\n \t\t},\n \t\t{\n \t\ttest: /\\.s[ac]ss$/i,\n \t\tuse: [\n \t\t\t// Creates `style` nodes from JS strings\n \t\t\t \"style-loader\",\n \t\t\t// Translates CSS into CommonJS\n \t\t\t\"css-loader\",\n \t\t\t// Compiles Sass to CSS\n \t\t\t\"sass-loader\",\n \t\t],\n \t\t},\n \t]\n \t}\n}","filename":"webpack.config.js"}

Tout va bien ?

Arthur, l'apprenti développeurJe te suis oui !
Alors maintenant, place aux tests.
Créons un fichier style.scss dans "src". Pour ma part j'ai fait ça :

{"language":"text/css","content":"$main-blue: blue;\n\nbody {\n background-color: $main-blue;\n div {\n \tcolor: $main-blue;\n \tbackground:gray;\n }\n}","filename":"style.scss"}

Ensuite, il faut importer le style dans un fichier JS :

{"language":"text/javascript","content":"import \"./style.scss\"\n\nlet a = \"bonjour\"\nconsole.log(a)","filename":"app.js"}

Et mon HTML :

{"language":"text/html","content":"<!DOCTYPE html>\n<html lang=\"fr\">\n<head>\n\t<meta charset=\"utf-8\">\n\t<title>Exemple loader</title>\n</head>\n<body>\n\t<div>Du texte</div>\n\t<script src=\"app.bundle.js\"></script>\n</body>\n</html>","filename":"index.html"}

Le résultat : une page avec un fond bleu et une div avec un fond gris et un texte bleu !

Arthur, l'apprenti développeurSuper ça marche !
Allez, un petit bonus, pour utiliser Vue.JS correctement avec Webpack (même s'il est recommandé d'utiliser vue-cli, l'outil de build dédié de Vue.JS)
{"language":"shell","content":"npm install -D vue-style-loader vue-loader@next vue-template-compiler css-loader && npm install vue@next","filename":""}

{"language":"text/javascript","content":"const path = require('path')\nconst webpack = require('webpack')\n\nconst { VueLoaderPlugin } = require(\"vue-loader\");\n\nmodule.exports = {\n\tmode: 'development',\n \tentry: {\n \t\tapp:'./src/app.js', \n \t},\n \toutput: {\n \t\tfilename:'[name].bundle.js',\n \tpath: path.resolve(__dirname, 'public')\n \t},\n \tmodule: {\n \trules: [\n \t\t{\n \t\ttest: /\\.js$/,\n \t\texclude: /node_modules/,\n \t\tuse: {\n \t\t\tloader: \"babel-loader\",\n \t\t\toptions: {\n \t\t\tpresets: ['@babel/preset-env']\n \t\t\t}\n \t\t}\n \t\t},\n \t\t{\n \t\ttest: /\\.vue$/,\n \t\tloader: 'vue-loader'\n \t\t},\n \t\t{\n \t\ttest: /\\.s[ac]ss$/i,\n \t\tuse: [\n \t\t\t// Creates `style` nodes from JS strings\n \t\t\t \"style-loader\",\n \t\t\t// Translates CSS into CommonJS\n \t\t\t\"css-loader\",\n \t\t\t// Compiles Sass to CSS\n \t\t\t\"sass-loader\",\n \t\t],\n \t\t},\n \t\t{\n \t\ttest: /\\.css$/i,\n \t\tuse: [\n \t\t\t\"vue-style-loader\",\n \t\t\t\"css-loader\",\n \t\t],\n \t\t},\n\n \t]\n \t},\n \tplugins: [\n \t// make sure to include the plugin for the magic\n \tnew VueLoaderPlugin(),\n \tnew webpack.DefinePlugin({\n \t\t\t'__VUE_OPTIONS_API__': JSON.stringify(true),\n \t\t\t'__VUE_PROD_DEVTOOLS__': JSON.stringify(false)\n\t\t})\n \t]\n}","filename":"webpack.config.js"}

Arthur, l'apprenti développeurMerci beaucoup ! Mais c'est quoi ça, plugins ? Je vois que tu en as utilisés.
C'est le titre de la prochaine partie ! Alors nous allons voir ça juste après. J'ai terminé cette partie
Demander de l'assistance