2019-10-17: Création
2019-10-27: Plusieurs bundler

2019-10-31: L'installation de Webpack finis par devenir une installation de ReactJS from scratch.
Cette procédure devient ma référence pour avoir RectJS sans create-react-app pour plus de modularité et facilité d'intégration dans un projet Django.

Installation de Webpack avec yarn from scratch

Créer un dossier qui deviendra le dossier projet de webpack

$ mkdir hello-webpack
$ cd hello-webpack
$ npm init -y

Version avec npm :

La commande npm init crée un fichier json avec la description du projet

package.json:

{
  "name": "hello-webpack",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

Version avec yarnpkg :

$ yarnpkg init

Le fichier package.json:

{
  "name": "hello-webpack",
  "version": "1.0.0",
  "description": "tuto webpack",
  "main": "index.js",
  "author": "JC_onLine",
  "license": "MIT"
}
Suite à recommandation lors d'une formation, je décide de continuer avec le gestionnaire de package yarn malgré l'utilisation de npm dans la doc de webpack à cette date
$ yarnpkg add webpack webpack-cli
$ ll
total 136
drwxr-xr-x 315 jc jc  12288 oct.  17 23:36 node_modules
-rw-r--r--   1 jc jc    231 oct.  17 23:36 package.json
-rw-r--r--   1 jc jc 120715 oct.  17 23:36 yarn.lock
$ cat package.json 
{
  "name": "hello-webpack",
  "version": "1.0.0",
  "description": "tuto webpack",
  "main": "index.js",
  "author": "JC_onLine",
  "license": "MIT",
  "dependencies": {
    "webpack": "^4.41.2",
    "webpack-cli": "^3.3.9"
  }
}

Lancement de Webpack

Il faut ajouter un script dans package.json pour spécifier l'appel de webpack:
Ici le script s’appellera build:

{
  "name": "hello-webpack",
  "version": "1.0.0",
  "description": "tuto webpack",
  "main": "index.js",
  "scripts": {
      "build": "webpack --mode development"
  },
  "author": "JC_onLine",
  "license": "MIT",
  "dependencies": {
    "webpack": "^4.41.2",
    "webpack-cli": "^3.3.9"
  }
}
L'option –mode development
permet d'avoir on code non minimifier donc lisible
Remplacer development par production pour la version finale de production

Les dossiers src et dist

Les dossiers par défaut sont :

  • Le dossier source: src
  • Le dossier résultat: build
$ tree -L 1
.
├── dist
├── node_modules
├── package.json
├── src
└── yarn.lock

Serveur de développement

Il est possible d'ajouter un serveur de développement :

$ yarnpkg add webpack-dev-server --dev
L'option –dev permet d'ajouter une rubrique de développement dans package.json :
"devDependencies": {
  "webpack-dev-server": "^3.8.2"
}

source: yarnpkg.com

Lancement du serveur

On peut le lancé un la main

$ yarnpkg webpack-dev-server

Ou créer un script dans package.json :
Source: yarnpkg.com/fr/docs/cli/run

"scripts": {
  "build": "webpack --mode development --watch",
  "dev": "webpack-dev-server --mode development"
},
L'option –watch permet le livebundling des fichiers JavaScript

Customisation de Webpack

Il faut créer un fichier webpack.config.js à la racine du projet :

const path = require('path');
 
module.exports = {
    entry: './src/index.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'bundle.js'
    }
}

Cette config spécifie les nom de fichiers d“entrées / sorties

Les Loaders

Les loaders permet un traitement sur des fichiers.

Babel-loader

Ce loader est utilisé pour transpiler du ECMA script 6 en ECMA script 5 pour les anciens navigateurs.
L'installation de babel-loader

$ yarnpkg add -D babel-loader @babel/core @babel/preset-env
Complément pour l'installation de React from scratch:
$yarnpkg add -D babel-core babel-loader babel-preset-env babel-preset-react html-webpack-plugin

La configuration de webpack :

const path = require('path');
 
module.exports = {
    entry: './src/index.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'bundle.js'
    },
    module:{
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: 'babel-loader'
            }
        ]
    }
}

Création du fichier .babelrc
c'est un simple objet json qui comporte les paramètres de réglages de babel:

{
    "presets": [
        "@babel/preset-env"
    ]
}

CSS Loader, Style Loader

Mise à jour de webpack.config.js

const path = require('path');
 
module.exports = {
    entry: './src/index.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'bundle.js'
    },
    module:{
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: 'babel-loader'
            },
            {
                test:/\.css$/,
                exclude: /node_modules/,
                use: ['style-loader', 'css-loader']
            }
        ]
    }
}

Installation des modules :

$ yarnpkg add -D css-loader
$ yarnpkg add -D style-loader

Cela met à jour le fichier package.json:

{
  "name": "hello-webpack",
  "version": "1.0.0",
  "description": "tuto webpack",
  "main": "index.js",
  "scripts": {
    "build": "webpack --mode development",
    "dev": "webpack-dev-server --mode development --watch"
  },
  "author": "JC_onLine",
  "license": "MIT",
  "dependencies": {
    "webpack": "^4.41.2",
    "webpack-cli": "^3.3.9"
  },
  "devDependencies": {
    "@babel/core": "^7.6.4",
    "@babel/preset-env": "^7.6.3",
    "babel-loader": "^8.0.6",
    "css-loader": "^3.2.0",
    "style-loader": "^1.0.0",
    "webpack-dev-server": "^3.8.2"
  }
}

Exemple d'appel css dans un fichier js:

import { add } from './math';
import './app.css';
 
const res = add(1 , 2);
console.log(res);

Sass Loader

La doc: sass-loader
La configuration de webpack.config.js :

const path = require('path');
 
module.exports = {
    entry: './src/index.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'bundle.js'
    },
    module:{
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: 'babel-loader'
            },
            {
                test:/\.css$/,
                exclude: /node_modules/,
                use: ['style-loader', 'css-loader']
            },
            {
                test: /\.s[ac]ss$/i,
                use: [
                // Creates `style` nodes from JS strings
                'style-loader',
                // Translates CSS into CommonJS
                'css-loader',
                // Compiles Sass to CSS
                'sass-loader',
                ],
            },
        ]
    }
}

Installation du loader :

$ yarnpkg add -D sass-loader node-sass

Cela met à jour le fichier package.json:

{
  "name": "hello-webpack",
  "version": "1.0.0",
  "description": "tuto webpack",
  "main": "index.js",
  "scripts": {
    "build": "webpack --mode development",
    "dev": "webpack-dev-server --mode development --watch"
  },
  "author": "JC_onLine",
  "license": "MIT",
  "dependencies": {
    "webpack": "^4.41.2",
    "webpack-cli": "^3.3.9"
  },
  "devDependencies": {
    "@babel/core": "^7.6.4",
    "@babel/preset-env": "^7.6.3",
    "babel-loader": "^8.0.6",
    "css-loader": "^3.2.0",
    "node-sass": "^4.12.0",
    "sass-loader": "^8.0.0",
    "style-loader": "^1.0.0",
    "webpack-dev-server": "^3.8.2"
  }
}

Exemple de code sass :

file.scss
 
$body-color: red;
 
body {
  color: $body-color;
}

Exemple d'appel du fichier file.scss :

import style from './style.scss';

Les Plugins

  • Les Loaders: Ils s'appliquent à des fichiers, il y a un test de l'extension, pour lui attribuer un taitement.
  • Les Plugins: Sont capables d'agir au niveau des Packages, des Bundles.

ProgressPlugin (intégré)

html-webpack-plugin (externe)

Ce plugin est utilisé dans l'installation de React from scratch.
Lors de l'utilisation avec Django le hash est désactivé pour des raisons de simplification d'intégration de React dans Django , car Django a ces propres templates.

Source: html-webpack-plugin
Principe: Générer une page html depuis le code js en s'appuyant sur un template html qui est dans le dossier src.
Le lien de la balise <script> vers le chemin js sera renseigné automatiquement avec le hash dans le nom du fichier js. Le hash dans le nom du fichier js sert à obliger le navigateur à recharger ce fichier et de pas utilisé le précédant qui est en cache.
Installation :

$ yarnpkg add -D html-webpack-plugin

Paramétrage dans webpack.config.js:

const path = require('path');
const webpack = require('webpack');
const htmlWebpackPlugin = require('html-webpack-plugin');
[...]
    module:{
        [...]
    },
    plugins: [
        new webpack.ProgressPlugin(),
        new htmlWebpackPlugin({
            hash: true,
            title: 'Webpack 4 :)',
            template: './src/index.html'
        })
    ]
}

Bootstrap

Installation de Bootstrap 4
Source: getbootstrap.com

$ yarnpkg add jquery popper popper.js bootstrap

Foundation Zurb

Installation de Foundation Zurb
Source: foundation.zurb.com

$ yarnpkg add what-input foundation-sites

Sass

Installation de Sass
Source: sass-lang.com

$ yarnpkg add sass

Compass + Mixins

Installation de compass
Source: npmjs.com/package/compass

$ yarnpkg add compass 

Installation de compass-mixins
Source: npmjs.com/package/compass-mixins

$ yarnpkg add compass-mixins 

React

Installation de create-react-app
Source: ReactJS

create-react-app n'est pas obligatoire si l'on souhaite installer React from scratch
$ yarnpkg add create-react-app

Source: How_to_set_up_React_webpack_and_Babel_writing_React_components

$ yarnpkg add react react-dom

Source: react-from-scratch-part-3

$ yarnpkg add -D babel-loader @babel/core @babel/preset-react

Avec mise à jour de webpack.config.js

[...]
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: {
                  loader: 'babel-loader',
                  options: {
                    presets: ['@babel/preset-react']
                  }
                }
            },
[...]

prop-types

babel-plugin-proposal-class-properties

Suite à l'erreur 'classProperties' isn't currently enabled il faut installer ce plugin
Source: github.com/.../babel-plugin-proposal-class-properties
Docs: babeljs.io/.../babel-plugin-proposal-class-properties

$ yarnpkg add @babel/plugin-proposal-class-properties --dev

Ajouter le paramétrage dans .babelrc:

{
    "presets": [
        "@babel/preset-env", "@babel/preset-react"
    ],
    "plugins": [
        ["@babel/plugin-proposal-class-properties", 
            {
                "loose": true 
            }
        ]
    ]
}

react-hot-loader

Source: blog.usejournal.com/creating-a-react-app-from-scratch
Docs: github.com/gaearon/react-hot-loader

$ yarnpkg add react-hot-loader

Mise à jour de .babelrc

// .babelrc
{
  "plugins": ["react-hot-loader/babel"]
}

Marquer le 'root component'

// App.js
import { hot } from 'react-hot-loader/root';
const App = () => <div>Hello World!</div>;
export default hot(App);

react-toastify

Permet d'afficher des notification dans une appli React :
Docs: www.npmjs.com/package/react-toastify
Démo visible ici : fkhadra.github.io/react-toastify

$ yarnpkg add react-toastify

Mise à jour de package-json

[...]
    "react-toastify": "^5.4.0",
[...]

Générer plusieurs bundles séparément

Source: html-webpack-plugin

  • Il faut remplacer l'entrée unique par un objet js:

Fichier webpack.config.js à entrée unique :

module.exports = {
    entry: './src/index.js',
    [...]
  • Fichier webpack.config.js à plusieurs entrée :
module.exports = {
    entry: {
        index: './src/index.js',
        tecnos: './src/tecnos.js'
    [...]

Pour nommer les fichiers bundles:

    [...]
    output: {
        path: path.resolve(__dirname, '..', 'dashboard', 'static-src',
            'js', 'react','dist'),
        filename: '[name].js'
    },
    [...]
  • Il est possible de générer séparément les fichiers html en modifiant le fichier webpack.config.js:
    [...]
    plugins: [
        new webpack.ProgressPlugin(),
        new webpack.htmlWebpackPlugin({
            title: 'Webpack 4 :)',
            template: '.src/index.html'
        })
        new webpack.htmlWebpackPlugin({
            filename: 'technos.html',
            template: '.src/technos.html'
        })
    ]
}
Attention:
Les 2 liens <script> sont présent dans les fichiers index.html et technos.html
Il faut donc supprimer manuellement le lien js inutile dans chaque fichiers html.

Webpack-bundle-analyzer

Source: webpack-bundle-analyzer Permet de voir si l'on peut factoriser des éléments bundlé plusieurs fois dans des fichiers différents.

  • Installation:
$ yarnpkg add -D webpack-bundle-analyzer
  • Modifications du fichier webpack.config.js :
[...]
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
[...]
    plugins: [
        new webpack.ProgressPlugin(),
        new htmlWebpackPlugin({
            title: 'Webpack 4 :)',
            template: '.src/index.html'
        })
        new htmlWebpackPlugin({
            filename: 'technos.html',
            template: '.src/technos.html'
        }),
        new BundleAnalyzerPlugin()
    ]
}

Webpack Shell Plugin

Ce plugin permet d'exécuter un shell script, avant ou après le build du bundle.
Source https://www.npmjs.com/package/webpack-shell-plugin

  • Installation :
$ yarnpkg add -D webpack-shell-plugin
  • Setup : webpack.config.js
const WebpackShellPlugin = require('webpack-shell-plugin');
 
module.exports = {
  ...
  ...
  plugins: [
    new WebpackShellPlugin({onBuildStart:['echo "Webpack Start"'], onBuildEnd:['echo "Webpack End"']})
  ],
  ...
}