AGNIESZKA LEŚKÓW

Konfiguracja Webpack 5 (sass, javascript ES6) na potrzeby projektu front-end

KOMPENDIUM

Konfiguracja Webpack 5 (sass, javascript ES6) na potrzeby projektu front-end

W celu usprawnienia pracy w środowisku Windows posłużymy się module bundler Webpack 5 czyli pakietem modułów łączącym, przekształcającym i pakującym moduły w jeden plik wynikowy pakietu JavaScript automatycznie umieszczanym w nagłówku strony internetowej.

Aby rozpocząć pracę z narzędziem Webpack 5 należy zainstalować Node.js (środowisko uruchomieniowo-wykonawcze JavaScript) z automatycznie instalowanym npm (pozwalającym instalować i odinstalowywać
pakiety i ich zależności – loadery, pluginy). Webpack parsuje (analizuje) kod plików wejściowych (ang. Entry Points) zawartych w pliku konfiguracyjnym webpack.config.js i buduje wykres zależności na podstawie pliku package.json. W ten sposób automatyzuje, optymalizuje zadania np. kompilując Sass do CSS, transpilując ES6+ do ES5 zrozumiałego dla przeglądarek, itd. Ma też na celu tworząc jeden plik wynikowy ograniczenie liczby żądań podczas ładowania kodu co poprawia wydajności takiej strony, aplikacji internetowej.

W Node.js każdy plik jest traktowany jako osobny moduł.
Node.js obsługuje dwa systemy modułów: moduły CommonJS i moduły ECMAScript, czyli zestaw standardów używanych do implementacji modułów JavaScript:

  1. Standard ECMAScript Modules poprzez użycie instrukcji import i export.
  2. Standard CommonJS modules poprzez funkcje require() i module.exports, nie obsługujący przeglądarek internetowych, głównie używany z Node w aplikacjach JavaScript po stronie serwera.

W pierwszym kroku zaczynamy od instalacji Node.js:
https://nodejs.org/en > Download for Windows (x64) 18.16.0 LTS Recommended For Most Users
Pobieramy a następnie instalujemy plik node-v18.16.0-x64.msi.
Sprawdzamy zainstalowane wersje poprzez:
cmd (Wiersz polecenia Windows) lub Git Bash Here (Powłoka terminalowa dla Windows,
zapewnia warstwę emulacji dla obsługi wiersza poleceń Git), z której będziemy korzystać w tym projekcie. Jeżeli nie mamy to musimy najpierw zainstalować Git: https://git-scm.com/.

node -v  # v18.16.0
npm -v   # 9.5.1

W tym projekcie zostaną wykorzystane:

  1. webpack webpack-cli
  2. html-webpack-plugin
  3. webpack-dev-server
  4. copy-webpack-plugin
  5. sass sass-loader style-loader css-loader
  6. postcss-loader postcss autoprefixer
  7. css-minimizer-webpack-plugin
  8. babel-loader @babel/core @babel/preset-env
  9. terser-webpack-plugin
  10. clean-webpack-plugin

Końcowa struktura katalogów i plików projektu starter-webpack-5, która posłuży nam za bazę startową do pracy nad projektem front-end będzie wyglądać następująco:

starter-webpack-5
    /dist (aplikacja wynikowa)
      /images
    • dog-paw-heart.jpg
    • dog-paw-heart.png
    • dog-paw-heart.webp
      bundle.min.js
      bundle.min.js.LICENSE.txt
      bundle.min.js.map
      index.html
    /src
      /images
    • dog-paw-heart.jpg
    • dog-paw-heart.png
    • dog-paw-heart.webp
      /javascript
    • app.js
    • index.js
      /scss
    • main.scss
      index.html
    /webpack
  • /node_modules
  • package-lock.json
  • package.json
  • webpack.config.js
    LICENSE
    README.md

Po przejściu do wybranego miejsca, w którym będziemy tworzyć nasz projekt otwieramy prawym przyciskiem myszy menu kontekstowe w Windows klikając w Git Bash Here i tworzymy główny katalog projektu z podstawowym zapleczem plików:

mkdir starter-webpack-5   # utworzenie katalogu
cd starter-webpack-5 # przejście do katalogu
pwd # sprawdzenie ścieżki /starter-webpack-5
code . # otwarcie projektu w edytorze VSCode

VSCode > Terminal > New Terminal # otwarcie terminala bezpośrednio w VSCode > bash

mkdir src # utworzenie katalogu źródła zasobu
cd src # przejście do katalogu
touch index.html # utworzenie pliku 
index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Starter Webpack 5 for front-end project</title>
</head>
<body>
    <h1>Webpack 5 for front-end application configuration</h1>
    <ol>
        <li>webpack webpack-cli</li>
        <li>html-webpack-plugin</li>
        <li>webpack-dev-server</li>
        <li>copy-webpack-plugin</li>
        <li>sass sass-loader style-loader css-loader</li>
        <li>postcss-loader postcss autoprefixer</li>
        <li>css-minimizer-webpack-plugin</li>
        <li>babel-loader @babel/core @babel/preset-env</li>
        <li>terser-webpack-plugin</li>
        <li>clean-webpack-plugin</li>
    </ol>
</body>
</html>
mkdir javascript # utworzenie katalogu
cd javascript # przejscie do katalogu
touch index.js # utworzenie pliku
Dla testu do index.js wpisujemy przykładowy kod JavaScript:
document.querySelector("h1").style.color = "#00FFFF";
document.addEventListener("click", function () {
  document.querySelector("h1").innerHTML = "TEST JavaScript";
}); 

W index.html dopisujemy ścieżkę przed </head>:

<script defer src="./javascript/index.js"></script>

Sprawdzamy poprawność wyświetlania kodu HTML z JavaScript otwierając w przeglądarce plik index.html. Nagłówek h1 został wyświetlony w kolorze aqua, a po kliknięciu w nagłówek zostaje zmieniona treść tekstu.

cd .. # wyjście z folderu javascript 
cd .. # wyjście z folderu src
pwd # katalog główny projektu /starter-webpack-5
mkdir webpack # utworzenie katalogu
cd webpack # przejście do katalogu
pwd # sprawdzenie ścieżki /starter-webpack-5/webpack

Konfigurację projektu rozpoczynamy od stworzenia pliku package.json odnotowującego opis wszystkich zależności.
Będąc w katalogu webpack inicjujemy nasz projekt:

npm init (z ustawianiem) || npm init -y (bez ustawiania)

w czego rezultacie powstaje właśnie ten plik package.json.

Z ustawieniami:

npm init ENTER # konfigurowanie, zainicjowanie pakietu, opisu zależności projektu
package name: (webpack) starter-webpack-5 ENTER
version: (1.0.0) ENTER
description: Starter Webpack 5 for front-end application ENTER
entry point: (index.js) ENTER
test command: ENTER
git repository: https://github.com/AnesseL/starter-webpack-5 ENTER
keywords: webpack javascript js es6 css sass scss ENTER 
author: Agnieszka Leśków ENTER
license: (ISC) MIT ENTER
yes ENTER
package.json
{
  "name": "starter-webpack-5",
  "version": "1.0.0",
  "description": "Starter Webpack 5 for front-end application",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/AnesseL/starter-webpack-5.git"
  },
  "keywords": [
    "webpack",
    "javascript",
    "js",
    "es6",
    "css",
    "sass",
    "scss"
  ],
  "author": "Agnieszka Leśków",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/AnesseL/starter-webpack-5/issues"
  },
  "homepage": "https://github.com/AnesseL/starter-webpack-5#readme"
}

Bez ustawienia:

npm init -y # -y === yes - ustawia w package.json wartości domyślne
package.json
{
  "name": "starter-webpack-5",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
}

Podczas pracy nad konfiguracją projektu, instalacji różnych zależności określamy status zapisu paczek przeznaczonych dla wersji deweloperskiej lub produkcyjnej.

-D === –save-dev === „devDependencies” – to status zależności developerskiej, są to paczki używane tylko dla developmentu. Podczas pracy nad projektem, stosujemy je tylko do budowania wersji deweloperskiej w większości składającej się z wtyczek (ang. plugins) a także ładowarek, wczytywarek (ang. loaders). Przekształcają one różne typy plików nie obsługiwanych domyślnie przez Webpack w moduły na użytek projektu) i nie są później dołączane do kodu produkcyjnego.

Natomiast „Dependencies” – to status zależności do budowania wersji produkcyjnej, w jego skład wchodzą m.in. biblioteki np. jQuery, respond.js, normalize.css, itd..

Zaczynamy instalowanie paczek lokalnie od bazy, która będzie tym zawiadywać:

npm i webpack webpack-cli -D
npm uninstall webpack webpack-cli # usunięcie stosujemy tylko wtedy gdy coś będziemy chcieli usunąć

webpack – to bundler, pakowacz, transpilator JS, potrafi spakować wiele różnych formatów do jednego pliku wynikowego JavaScript
webpack-cli – interfejs wiersza polecenia pakietu webpack, klient webpack

W tym procesie instalacji powstał katalog z pakietami: node_modules i plik package-lock.json

package.json
{
  "name": "starter-webpack-5",
  "version": "1.0.0",
  "description": "Starter Webpack 5 for front-end application",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/AnesseL/starter-webpack-5.git"
  },
  "keywords": [
    "webpack",
    "javascript",
    "js",
    "es6",
    "css",
    "sass",
    "scss"
  ],
  "author": "Agnieszka Leśków",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/AnesseL/starter-webpack-5/issues"
  },
  "homepage": "https://github.com/AnesseL/starter-webpack-5#readme",
  "devDependencies": {
    "webpack": "^5.84.1",
    "webpack-cli": "^5.1.1"
  }
}

W tym samy katalogu webpack tworzymy plik konfiguracyjny:

touch webpack.config.js # utworzenie pliku konfiguracyjnego

webpack.config.js – to moduł Node.js wymaga użycia Standard CommonJS modules
poprzez funkcje require() i module.exports, a wszystko czego nie wpiszemy do tego pliku przyjmie wartość domyślną. Więc wpisujemy podstawowe ustawienia.

webpack.config.js
const path = require('path'); //podstawowy moduł Node.js

module.exports = {
  mode: 'development', //Określone --mode dla skryptu
  entry: path.resolve(__dirname, '../src/javascript/index.js'), //Domyślny punkt wejscia
  output: {
    path: path.resolve(__dirname, '../dist'), //Punkt wyjścia
    filename: 'bundle.js',
  }
};

A w pliku package.json konfigurujemy rozruch:

package.json 
  "scripts": {
    "dev": "webpack",
    "watch": "webpack --watch",
    "prod": "webpack --mode production"
  },

Po zainstalowaniu bazy najlepszym i najprostszym rozwiązaniem będzie zainstalowanie wtyczki html-webpack-plugin aby móc sprawdzić poprawność wyświetlania kodu HTML z JS.

Podczas pracy nad tym projektem należy pamiętać aby każda instalacja i rozruch odbywały się w folderze webpack.

cd webpack # przejście do folderu jeżeli jesteśmy w innym miejscu
npm i html-webpack-plugin -D
npm uninstall html-webpack-plugin # usunięcie

html-webpack-plugin – webpack podczas procesu pakowania automatycznie i dynamiczne generuje w katalogu wynikowym /dist plik .html wstrzykując w nagłówek strony internetowej link do zasobu:

<script defer src="bundle.js"></script></head>
package.json
  "devDependencies": {
    "html-webpack-plugin": "^5.5.1",
    "webpack": "^5.84.1",
    "webpack-cli": "^5.1.1"
  }

Aby ta wtyczka mogła zacząć spełniać swoje zadania musimy ją wskazać w pliku konfiguracyjnym:

const HtmlWebpackPlugin = require('html-webpack-plugin') //import pluginu
i
plugins: [
  new HtmlWebpackPlugin({
    filename: 'index.html',
    template: '../src/index.html'
  })
]
webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');

module.exports = {
  mode: 'development',
  entry: path.resolve(__dirname, '../src/javascript/index.js'),
  output: {
    path: path.resolve(__dirname, '../dist'),
    filename: 'bundle.js',
  },
  plugins: [
    new HtmlWebpackPlugin({
      filename: 'index.html',
      template: '../src/index.html'
    })
  ]
};

Natomiast w pliku src/index.html zakomentowujemy:

<!-- <script defer src="./javascript/index.js"></script> -->
npm run dev || npm run watch (wyjście Ctrl + C z trybu śledzenia) || npm run prod # trzy wersje rozruchu skryptu

Po wykonaniu rozruchu npm run dev, webpack wygeneruje nam katalog wynikowy /dist z plikami: bundle.js i index.html.

Po otwarciu wygenerowanego pliku w /dist/index.html możemy zauważyć, iż wtyczka html-webpack-plugin sama dynamicznie i automatycznie wstawiła do nagłówka strony przed </head> ścieżkę do wygenerowanego pliku javascript:

<script defer src="bundle.js"></script></head>

Teraz w ramach kontroli sprawdzamy plik /dist/index.html w przeglądarce
czy wyświetla się prawidłowo. Wszystko działa poprawnie tak samo jak w powyższym teście.

Możemy też wykonać testy dla rozruchu npm run watch lub npm run prod, tylko musimy pamiętać o uprzednim każdorazowym usunięciu katalogu wynikowego /dist, aby zobaczyć efekty.

npm run watch # wyjście z trybu śledzenia Ctrl+C
||
npm run prod

tryb –watch pozwala na prace ciągłą tzn. po każdorazowej zmianie automatycznie zapisze do pliku wynikowego

Plik /dist/index.html wyświetla się tak samo jak wcześniej.

W następnym kroku dla usprawnienia pracy pochylimy się nad webpack-dev-server – webpack z serwerem programistycznym, który zapewni nam tryb obserwowania i automatyczne przeładowanie kodu na żywo, dzięki czemu zawartość naszego projektu będzie automatycznie odświeżana w przeglądarce.

npm i webpack-dev-server -D
npm uninstall webpack-dev-server # usunięcie
package.json
  "devDependencies": {
    "html-webpack-plugin": "^5.5.1",
    "webpack": "^5.84.1",
    "webpack-cli": "^5.1.1",
    "webpack-dev-server": "^4.15.0"
  }

Dopisuje rozruch dla webpack-dev-server:

package.json
  "scripts": {
    "dev": "webpack",
    "watch": "webpack --watch",
    "prod": "webpack --mode production",
    "serve": "webpack serve"
  },

Wprowadzamy ustawienia:

webpack.config.js
  devServer: {
    static: path.resolve(__dirname, '../dist'),
    compress: true,
    port: 9000,
    hot: true
  },
webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');

module.exports = {
  mode: 'development',
  entry: path.resolve(__dirname, '../src/javascript/index.js'),
  output: {
    path: path.resolve(__dirname, '../dist'),
    filename: 'bundle.js',
  },
  devServer: {
    static: path.resolve(__dirname, '../dist'),
    compress: true,
    port: 9000,
    hot: true
  },
  plugins: [
    new HtmlWebpackPlugin({
      filename: 'index.html',
      template: '../src/index.html'
    })
  ]
};
npm run serve # rozruch (Ctrl + C - wyjście)

W tym przypadku gdy usuniemy katalog /dist z plikami wynikowymi nie będą one dla nas widoczne, gdyż webpack-dev-server przechowuje je w pamięci.

Pomimo niewidocznych plików wynikowych projekt będzie dostępny dla nas pod adresem http://localhost:9000/. Od teraz a właściwie od momentu rozruchu npm run serve w trakcie pracy i każdroazowej zmianie w kodzie wszystkie zmiany bedą przeładowywane na żywo.

Teraz czas na kolejną wytczkę copy-webpack-plugin stosowaną najczęściej dla wersji produkcyjnej, która kopiuje katalogi, pliki do katalogu wynikowego /dist.

W katalogu src zakładamy katalog /images dodąjac pliki z rozszerzeniem: .jpg, .png, .webp:

cd .. # wyjście z katalogu webpack
cd src # wejście do katalogu src
mkdir images # utworzenie katalogu 

W katalogu /images dodajemy pliki z przykładowymi rozszerzeniami:
.webp, .png, .jpg. Natomiast w pliku src/index.html wprowadzamy grafiki:

index.html
    <div class="images">
        <figure>
            <img src="./images/dog-paw-heart.webp" alt="Dog's paw in the heart" style="max-width:100%; height: auto;">
            <figcaption>Dog's paw in the heart.</figcaption>
        </figure>
        <figure>
            <img src="./images/dog-paw-heart.png" alt="Dog's paw in the heart" style="max-width:100%; height: auto;">
            <figcaption>Dog's paw in the heart.</figcaption>
        </figure>
        <figure>
            <img src="./images/dog-paw-heart.jpg" alt="Dog's paw in the heart" style="max-width:100%; height: auto;">
            <figcaption>Dog's paw in the heart.</figcaption>
        </figure>
    </div>

Przechodzimy do instalacji:

cd .. # wyjście z katalogu src
cd webpack # wejście do katalogu webpack
npm i copy-webpack-plugin -D
npm uninstall copy-webpack-plugin
package.json
  "devDependencies": {
    "copy-webpack-plugin": "^11.0.0",
    "html-webpack-plugin": "^5.5.1",
    "webpack": "^5.84.1",
    "webpack-cli": "^5.1.1",
    "webpack-dev-server": "^4.15.0"
  }

Przechodzimy do konfiguracji:

webpack.config.js
const  CopyPlugin  =  require ( "copy-webpack-plugin" );
module.exports = {
  plugins: [
    new CopyPlugin({
      patterns: [
        { from: "../src/images", to: "../dist/images" }
      ],
    }),
  ],
};
webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const  CopyPlugin  =  require ( "copy-webpack-plugin" );
const path = require('path');

module.exports = {
  mode: 'development',
  entry: path.resolve(__dirname, '../src/javascript/index.js'),
  output: {
    path: path.resolve(__dirname, '../dist'),
    filename: 'bundle.js',
  },
  devServer: {
    static: path.resolve(__dirname, '../dist'),
    compress: true,
    port: 9000,
    hot: true
  },
  plugins: [
    new HtmlWebpackPlugin({
      filename: 'index.html',
      template: '../src/index.html'
    }),
    new CopyPlugin({
      patterns: [
        { from: "../src/images", to: "../dist/images" }
      ],
    })
  ]
};

Wykonujemy przykładowy rozruch npm run dev i otrzymujemy pliki graficzne w katalogu wynikowym /dist:

Nareszcie przyszedł czas na preprocesor CSS.

cd .. # przejście do głównego katalogu starter-webpack-5
pwd # ścieżka /starter-webpack-5
cd src # przejście do folderu src

W katalogu src tworzymy katalog scss z plikiem main.scss:

mkdir scss # utworzenie katalogu
cd scss # przejscie do katalogu
pwd # sprawdzenie ścieżki /webpack-wp/scss
touch main.scss # utworzenie pliku main.scss

Tworzymy w pliku main.scss przykładowe ostylowanie:

main.scss
$red: red;
$green: green;
$blue: blue;
$coral: coral;
$gray: gray;
$white: white;
$black: black;
$bg: $gray;
body {
    background: $bg;
    display: flex;
    flex-direction: column;
    flex-wrap: wrap;
    justify-content: center;
    align-items: center;
    align-content: center;
    row-gap: 10px;
    column-gap: 10px;
    transition: all 300ms ease-in;
}
h1 {
    color: $red;
}
h2 {
    color: $green;
}
h3 {
    color: $blue;
}
h4 {
    color: $coral;
}
h5 {
    color: $white;
}
h6 {
    color: $black;
}
cd .. # przejście do katalogu src
cd .. # przejście do głównego katalogu starter-webpack-5
cd webpack # przejście do katalogu webpack
pwd # sprawdzamy ścieżkę /starter-webpack-5/webpack

Bedąc w katalogu webpack instalujemy wszystko co będzi potrzebne do przetwarzania sass:

npm i sass sass-loader style-loader css-loader -D
npm uninstall sass sass-loader style-loader css-loader # usunięcie

sass – preprocesor CSS
sass-loader – ładuje, obsługuje pliki typu sass/scss, współpracuje z sass, node-sass
style-loader – zmienia kod CSS na JavaScript, ładuje style, automatycznie dodaje style do nagłówka strony
css-loader – ładuje, wczytuje, przetwarza, zwraca, obsługuje zawartośc pliku .css
Wszystko zostanie uruchomione od dołu do góry:
góra
’style-loader’
’css-loader’
’sass-loader’
dół

package.json
  "devDependencies": {
    "copy-webpack-plugin": "^11.0.0",
    "css-loader": "^6.8.1",
    "html-webpack-plugin": "^5.5.1",
    "sass": "^1.62.1",
    "sass-loader": "^13.3.1",
    "style-loader": "^3.3.3",
    "webpack": "^5.84.1",
    "webpack-cli": "^5.1.1",
    "webpack-dev-server": "^4.15.0"
  }

W /src/javascript/index.js wstawiam na samej górze:

import "../scss/main.scss";
index.js
import "../scss/main.scss";

// document.querySelector("h1").style.color = "#00FFFF";
// document.addEventListener("click", function () {
//   document.querySelector("h1").innerHTML = "TEST JavaScript";
// }); 
webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const  CopyPlugin  =  require ( "copy-webpack-plugin" );
const path = require('path');

module.exports = {
  mode: 'development',
  entry: path.resolve(__dirname, '../src/javascript/index.js'),
  output: {
    path: path.resolve(__dirname, '../dist'),
    filename: 'bundle.js',
  },
  devServer: {
    static: path.resolve(__dirname, '../dist'),
    compress: true,
    port: 9000,
    hot: true
  },
  module: {
    rules: [
      {
        test: /\.css$/i, 
        use: [ "style-loader", "css-loader"],
      },
      {
        test: /\.s[ac]ss$/i,
        use: [
          {
           // Creates `style` nodes from JS strings (plugin instead of "style-loader")
            loader:  "style-loader",
            options: {}
          },
          {
            // Translates CSS into CommonJS
            loader: 'css-loader',
            options: {}
          },
          {
            // Compiles Sass to CSS
            loader: 'sass-loader',
            options: {
              implementation: require('sass'), // Prefer `dart-sass`
            },
          }, 
        ],
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      filename: 'index.html',
      template: '../src/index.html'
    }),
    new CopyPlugin({
      patterns: [
        { from: "../src/images", to: "../dist/images" }
      ],
    })
  ]
};

Sprawdzamy konfigurację preprocesora sass:

npm run dev || npm run watch (wyjście Ctrl + C z trybu śledzenia) || npm run prod

Otwierając w przeglądarce wynikowy index.html widzimy, że nagłówek zmienił kolor na czerwony czyli zostało zastosowane stylowanie z pliku main.scss.

Następnie dokładamy istotne detale:

npm i postcss-loader postcss autoprefixer -D
npm uninstal postcss-loader postcss autoprefixer # usuwanie

postcss-loader:

  • – moduł ładując postcss
  • – do przetwarzania CSS za pomocą postcss

postcss:

  • – pobiera plik CSS jako dane wejściowe, uruchamia wtyczki, kompiluje SASS do CSS, przekształca CSS w JavaScript z pomocą wtyczek js
  • – aby działał najpierw należy skonigurować w Webpack’u CSS
  • – posiada ogromną liczbę pluginów

autoprefixer:

  • – wtyczka postcss przekształcająca CSS poprzez JS
  • – analizuje css i automatycznie dodaje prefiksy dostawców do CSS, używając danych z Can I Use (-webkit, -moz, itp.)

Webpack zawsze zaczyna od końca najpierw uruchomi PostCSS na pliku CSS postcss-loader

Ważne jest aby wszystko działało poprawnie więc powinniśmy ustawić w pliku package.json liste do określenia przeglądarek:

Można ustawić wartość domyślną Webpack:

package.json:
  "browserslist": [ 
    "defaults"
  ],

Lub z własnymi ustawieniami, gdzie można zastosować dla przeglądarek określone wartości:

package.json:
"browserslist": [
  "last 2 version",
  ">0.5%",
  "Firefox ESR",
  "not dead"
],

„last 2 version” – ostatnie 2 wersje przeglądarek
„>0.5%” – więcej niż 0.5% używa tych przeglądarek,
„Firefox ESR” – kanał ESR oferujący tylko poprawki stabilności i bezpieczeństwa Firefox i nic więcej
„not dead” – przeglądarki nadal używane

W tym projekcie zastosujemy:

package.json:
  "browserslist": [
    "defaults"
  ],
  "devDependencies": {
    "autoprefixer": "^10.4.14",
    "copy-webpack-plugin": "^11.0.0",
    "css-loader": "^6.8.1",
    "html-webpack-plugin": "^5.5.1",
    "postcss": "^8.4.24",
    "postcss-loader": "^7.3.2",
    "sass": "^1.62.1",
    "sass-loader": "^13.3.1",
    "style-loader": "^3.3.3",
    "webpack": "^5.84.1",
    "webpack-cli": "^5.1.1",
    "webpack-dev-server": "^4.15.0"
  }

Uzupełniamy webpack.config.js o postcss-loader i autoprefixer:

webpack.config.js
module: {
    rules: [
      {
        test: /\.css$/i, 
        use: [ "style-loader", "css-loader", "postcss-loader"],
      },
      {
        test: /\.s[ac]ss$/i,
        use: [
          {
            loader: "postcss-loader",
            options: {
                postcssOptions: {
                    plugins: [
                        [
                            "autoprefixer",
                        ],
                    ],
                },
            },
          }, 
        ],
      },
    ],
  },
npm run dev || npm run watch (wyjście Ctrl + C z trybu śledzenia) || npm run prod

W celu sprawdzenia czy autoprefixer działa jak należy, wykonujemy rozruch npm run dev a następnie otwieramy wynikowy plik /dist/index.js i odnajdujemy auto-prefix np. -moz-.

Możemy również skorzystać z mapy źródłowej oferowanej przez Webpack dla skomilowanego kodu.

Aby wygenerować mapę źródłową za pomocą Webpack należy wprowadzić w pliku webpack.config.js wartość source-map dla devtool.

webpack.config.js
devtool: "source-map", 

Dzięki tej wartości Webpack dołącza plik z mapą, która mapuje skompilowany kod na pliki źródłowe. Dzięki temu badając JavaScript w przeglądarce debugger będzie nam wskazywał ścieżki do plików źródłowych.

W wyniku rozruchu zostaje wygenerowany plik wynikowy bundle.js.map.

npm i babel-loader @babel/core @babel/preset-env -D
npm uninstall babel-loader @babel/core @babel/preset-env # usunięcie

babel-loader – ładowarka umożliwiająca transpilację plików JavaScript przy użyciu Babel i webpack
@babel/core – rdzeń kompilatora (transpilacja (kompilacja) / tłumaczenie kodu ES6+ do ES5),
uruchamia kompilator babel, który transpiluje kod JavaScript ES6+ do ES5
@babel/preset-env – zbiór wtyczek i podstawowych gotowych ustawień, podobny do autoprefixer, również korzysta z browserlist tak jak autoprefixer

Tak wstępnie ustawione podstawowe środowisko instaluje wszystkie wtyczki ES6.

Aby pracować np. z React.js należy doinstalować dodatkową wtyczkę @babel/preset-react,
z TypeScript JS @babel/preset-typescript, natomiast z Vue.js babel-preset-vue
lub @vue/babel-preset-app, itd..

Ten zestaw wymaga ustawienia w package.json „browserslist”, my ustawiliśmy już jej wartość na:

package.json
"browserslist": [
    "defaults"
],

+

webpack.config.js
module: {
  rules: [
     {
        test: /\.m?js$/,
        exclude: /node_modules/,
        use: {
           loader: "babel-loader",
           options: {
              ['@babel/preset-env', { targets: "defaults" }]
           }
        }
     }
  ]
}
package.json 
  "browserslist": [
    "defaults"
  ],
  "devDependencies": {
    "@babel/core": "^7.22.1",
    "@babel/preset-env": "^7.22.4",
    "autoprefixer": "^10.4.14",
    "babel-loader": "^9.1.2",
    "copy-webpack-plugin": "^11.0.0",
    "css-loader": "^6.8.1",
    "html-webpack-plugin": "^5.5.1",
    "postcss": "^8.4.24",
    "postcss-loader": "^7.3.2",
    "sass": "^1.62.1",
    "sass-loader": "^13.3.1",
    "style-loader": "^3.3.3",
    "webpack": "^5.84.1",
    "webpack-cli": "^5.1.1",
    "webpack-dev-server": "^4.15.0"
  }

W katalogu src/javascript/ tworzymy dodatkowy plik app.js.

cd .. # wyjście z katalogu webpack
cd src # wejście do katalogu src
cd javascript # wejście do katalogu javascript
touch app.js # utworzenie pliku

W pliku src/javascript/app.js wpisujemy przykadowy kod JavaScript:

app.js
export const showParagraph = () => {
    const paragraph = document.createElement('p');
    paragraph.textContent = "babel-loader @babel/core @babel/preset-env";
    paragraph.style.color = "#00ff00";
    document.body.appendChild(paragraph);
};

function checkFunction() {
    alert("checkFunction() it's OK");
}
export {
    checkFunction
};

Natomiast w pliku src/javascript/index.js import z plik app.js:

index.js
import { showParagraph } from './app';
showParagraph();

import { checkFunction } from "./app";
checkFunction();

import "../scss/main.scss";

// document.querySelector("h1").style.color = "#00FFFF";
// document.addEventListener("click", function () {
//   document.querySelector("h1").innerHTML = "TEST JavaScript";
// }); 

Przechodzimy do wykonania rozruchu:

cd .. # wyjście z katalogu javascript
cd src # wejście do katalogu src
cd webpack # wejście do katalogu javascript
npm run dev || npm run watch (wyjście Ctrl + C z trybu śledzenia) || npm run prod

W pierwszej kolejności po otwarciu pliku index.html w przeglądarce zgłasza się alert, natomiast po kliknięciu w OK widzimy dodany napis w kolorze linomki.

Przyszedł już czas na minimalizację pliku wynikowego /dist/bundle.js.

cd webpack # wejście do katalogu webpack
npm i terser-webpack-plugin -D
npm uninstall terser-webpack-plugin # usunięcie

terser-webpack-plugin – ta wtyczka służy do minimalizacji kodu JavaScript

package.json
  "devDependencies": {
    "@babel/core": "^7.22.1",
    "@babel/preset-env": "^7.22.4",
    "autoprefixer": "^10.4.14",
    "babel-loader": "^9.1.2",
    "copy-webpack-plugin": "^11.0.0",
    "css-loader": "^6.8.1",
    "html-webpack-plugin": "^5.5.1",
    "postcss": "^8.4.24",
    "postcss-loader": "^7.3.2",
    "sass": "^1.62.1",
    "sass-loader": "^13.3.1",
    "style-loader": "^3.3.3",
    "terser-webpack-plugin": "^5.3.9",
    "webpack": "^5.84.1",
    "webpack-cli": "^5.1.1",
    "webpack-dev-server": "^4.15.0"
  }

W pliku webpack.config.js zmieniamy nazwę pliku wyjściowego na bundle.min.js a także dopisujemy ustawienia konfiguracyjne przeznaczone właśnie dla powyższej wtyczki.

webpack.config.js
const TerserPlugin = require("terser-webpack-plugin");

wraz z:

webpack.config.js
module.exports = {
  output: {
    path: path.resolve(__dirname, '../dist'),
    filename: 'bundle.min.js',
  },
  optimization: {
    minimize: true,
    minimizer: [
      new TerserPlugin({
        test: /\.m?js(\?.*)?$/i,
      }),
    ],
  },
};
webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyPlugin = require("copy-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin");
const path = require('path');

module.exports = {
  mode: 'development',
  entry: path.resolve(__dirname, '../src/javascript/index.js'),
  output: {
    path: path.resolve(__dirname, '../dist'),
    filename: 'bundle.min.js',
  },
  devtool: "source-map",
  devServer: {
    static: path.resolve(__dirname, '../dist'),
    compress: true,
    port: 9000,
    hot: true
  },
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: ["style-loader", "css-loader", "postcss-loader"],
      },
      {
        test: /\.s[ac]ss$/i,
        use: [{
            // Creates `style` nodes from JS strings (plugin instead of "style-loader")
            loader: "style-loader",
            options: {}
          },
          {
            // Translates CSS into CommonJS
            loader: 'css-loader',
            options: {}
          },
          {
            // Compiles Sass to CSS
            loader: 'sass-loader',
            options: {
              implementation: require('sass'), // Prefer `dart-sass`
            },
          },
          {
            loader: "postcss-loader",
            options: {
              postcssOptions: {
                plugins: [
                  [
                    "autoprefixer",
                  ],
                ],
              },
            },
          },
        ],
      },
      {
        test: /\.m?js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: [
              ['@babel/preset-env', { targets: "defaults" }]
            ]
          }
        }
      }
    ],
  },
  optimization: {
    minimize: true,
    minimizer: [
      new TerserPlugin({
        test: /\.m?js(\?.*)?$/i,
      }),
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      filename: 'index.html',
      template: '../src/index.html'
    }),
    new CopyPlugin({
      patterns: [{
        from: "../src/images",
        to: "../dist/images"
      }],
    }),
  ]
};

Zastosujemy teraz wtyczkę clean-webpack-plugin, która czyści automatycznie folder wynikowy /dist, usuwa zalegające zbędne katalogi, pliki, itd..

npm i clean-webpack-plugin -D
npm uninstall clean-webpack-plugin # usunięcie
package.json
"devDependencies": {
    "@babel/core": "^7.22.1",
    "@babel/preset-env": "^7.22.4",
    "autoprefixer": "^10.4.14",
    "babel-loader": "^9.1.2",
    "clean-webpack-plugin": "^4.0.0",
    "copy-webpack-plugin": "^11.0.0",
    "css-loader": "^6.8.1",
    "html-webpack-plugin": "^5.5.1",
    "postcss": "^8.4.24",
    "postcss-loader": "^7.3.2",
    "sass": "^1.62.1",
    "sass-loader": "^13.3.1",
    "style-loader": "^3.3.3",
    "terser-webpack-plugin": "^5.3.9",
    "webpack": "^5.84.1",
    "webpack-cli": "^5.1.1",
    "webpack-dev-server": "^4.15.0"
  }

Wprowadzamy ustawienia do pliku konfiguracyjnego:

webpack.config.js
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

oraz:

webpack.config.js
plugins: [
     new CleanWebpackPlugin(),
],
webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyPlugin = require("copy-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin");
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const path = require('path');

module.exports = {
  mode: 'development',
  entry: path.resolve(__dirname, '../src/javascript/index.js'),
  output: {
    path: path.resolve(__dirname, '../dist'),
    filename: 'bundle.min.js',
  },
  devtool: "source-map",
  devServer: {
    static: path.resolve(__dirname, '../dist'),
    compress: true,
    port: 9000,
    hot: true
  },
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: ["style-loader", "css-loader", "postcss-loader"],
      },
      {
        test: /\.s[ac]ss$/i,
        use: [{
            // Creates `style` nodes from JS strings (plugin instead of "style-loader")
            loader: "style-loader",
            options: {}
          },
          {
            // Translates CSS into CommonJS
            loader: 'css-loader',
            options: {}
          },
          {
            // Compiles Sass to CSS
            loader: 'sass-loader',
            options: {
              implementation: require('sass'), // Prefer `dart-sass`
            },
          },
          {
            loader: "postcss-loader",
            options: {
              postcssOptions: {
                plugins: [
                  [
                    "autoprefixer",
                  ],
                ],
              },
            },
          },
        ],
      },
      {
        test: /\.m?js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: [
              ['@babel/preset-env', { targets: "defaults" }]
            ]
          }
        }
      }
    ],
  },
  optimization: {
    minimize: true,
    minimizer: [
      new TerserPlugin({
        test: /\.m?js(\?.*)?$/i,
      }),
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      filename: 'index.html',
      template: '../src/index.html'
    }),
    new CopyPlugin({
      patterns: [{
        from: "../src/images",
        to: "../dist/images"
      }],
    }),
    new CleanWebpackPlugin(),
  ]
};
npm run dev || npm run watch (wyjście Ctrl + C z trybu śledzenia) || npm run prod

Po usunięciu np. wtyczki wykonujemy rozruch i sprawdzamy przy tej okazji wynikowy plik index.html w przegladarce.

Końcowy zrzut wszystkich katalogów z plikami projektu starter-webpack-5:

Gotowy projekt można zobaczyć pod adresem: https://github.com/AnesseL/starter-webpack-5.

Wróć do Kompendium