Skip to content

Kanishkar J

Configuring Webpack 4 (Part 3) Loaders and Plugins

Web Development, Webpack5 min read

Hello everyone, in the last post we installed webpack and setup webpack to just start the processes. In this post, we’ll set up webpack to read ES6/ES7 and transcompile them back to ES5. Then we’ll process SASS to CSS, and to read HTML files. First let’s learn what are loaders and plugins.

Loaders: They allow one to load files other than javascript and process them. Loaders can be configured by defining 2 things test and use. test defines what type of files will be selected and use defines which loader will operate over it. Eg :

1{
2 test: /\.js$/,
3 use: 'raw-loader'
4}

Plugins: Loaders are specific to particular file types. But plugins are more generalized and powerful. They can be used to perform tasks like minification. They can be used by importing them and adding them to the plugins array. The instances of the plugins are configurable.

First, we define the module object right after the output object in webpack.common.js. then inside it, we define an array called rules which will consist of configuration for each loader.

1output: {
2 // ... Other Config ...
3},
4module: {
5 rules: [
6 // Loaders are defined in here
7 ]
8}

Setting up Javascript: ES7 to ES5

We shall use the babel-loader package to read ES7 js files. To install babel-loader and few other packages dependent on it, execute the following command on the command line :

1npm install -D babel-loader babel-core babel-polyfill babel-preset-env

Now we will define a Loader to read javascript and transcompile it from ES7 to ES5( latest standard supported by all the browsers). let’s add a config object inside the rules array :

1module: {
2 rules: [
3 {
4 test: /\.js$/,
5 include: /src/,
6 exclude: /node_modules/,
7 use: ['babel-loader']
8 }
9 ]
10}

test : This attribute defines what kind of file extensions should webpack parse for this particular loader. It is defined using a Regular Expression. Here we are parsing Javascript files hence we write /.js$/.

include : This defines the directory webpack should look into (referenced from project root).

exclude : This defines which directory the loader should avoid while parsing.

use : This tells which loader it should use. Here the use attribute is defined in a simple way. The actual configuration of babel-loader is done in .babelrc :

.babelrc

1{
2 "presets": ["env"]
3}

That is a basic configuration, for more information about configuring babel check this.

Now let’s test if our configuration works. enter the following code inside /src/assets/app.js :

1let temp = 5;
2const func = (val) => console.log(val);
3
4func(temp);

Then run npm buildon your command line. Open the /dist/ folder, and you can see bundled files in it. Now open src/assets/app.js and search for console.log(5), you should find it. If you noticed above, the code we wrote was written in ES7 but the bundled code is in ES5.

Clear dist directory

Now make some changes to app.js and run npm build again. The changes will not be visible in the bundled code. Try deleting the dist directory and try again, you’ll see that the changes are reflected in the bundled code. Hence every time we bundle the code; we have to clear the dist directory manually which is a tiresome task to do. To resolve this issue we’ll use a plugin clean-webpack-plugin. First let’s install it, run the following in your command line :

1npm i -D clean-webpack-plugin

Now import the plugin in webpack.common.js :

1// ... other imports
2const CleanWebpackPlugin = require('clean-webpack-plugin');
3
4const config = {
5 // ... config
6}

Plugins are defined in an array inside the config object. Now inside this array we instantiate the CleanWebpackPlugin object with the directory to clean as a parameter (dist).

1const config = {
2 // ... config
3 module: {
4 // ...
5 },
6 plugins: [
7 new CleanWebpackPlugin(['dist'])
8 ]
9}

For more information on CleanWebpackPlugin check out the docs here.

Until now we only we only have bundled JS files, what about other files such as HTML and CSS ? let’s get to it.

Reading HTML files

This is how we configure webpack to read HTML files. First webpack reads the HTML file as string, then it prints the string back into a HTML file in the directory specified. Now how do we do this? we’ll do this with the help of a loader and a plugin :

html-loader : Exports HTML as string. HTML is minimized when the compiler demands.

html-webpack-plugin : Simplifies creation of HTML files to serve your webpack bundles.

let’s install them :

1npm i -D html-loader html-webpack-plugin

The config for HTML loader. It is similar to the config we wrote for js :

1module: {
2 // ...
3 {
4 test: /\.(html)$/,
5 use: {
6 loader: 'html-loader',
7 options: {
8 minimize: true,
9 removeComments: true,
10 collapseWhitespace: true
11 }
12 }
13 },
14 },

use.options.minimize : To minify the html files.

use.options.removeComments : All the comments in the HTML file will be discarded.

use.options.collapseWhitespace : All whitespaces will be removed.

The Above configuration will reduce the bundle size. Hence will have a great impact on the performance.

Import html-webpack-plugin in webpack.common.js :

webpack.common.js

1const HtmlWebpackPlugin = require('html-webpack-plugin');

Then config for html plugin :

1plugins: [
2 new HtmlWebpackPlugin({
3 filename: 'index.html',
4 template: 'index.html'
5 }),
6 ]

Here we specify two attributes :

filename : It Specified the output filename.

template : It Specified the input filename

Now to test whether everything is working, first write the following in index.html :

1<!DOCTYPE html>
2<html>
3<head>
4 <meta charset="utf-8" />
5 <meta http-equiv="X-UA-Compatible" content="IE=edge">
6 <title>Page Title</title>
7 <meta name="viewport" content="width=device-width, initial-scale=1">
8</head>
9<body>
10</body>
11</html>

Then run npm run in the command line. Now if you notice carefully, the dist directory is being deleted and created again. Also index.html is created, if you open it and compare it with the index.html of /src/. You will notice webpack added an additional line :

1<script type="text/javascript" src="assets/js/app.bundle.js"></script>

Thats webpack linking the bundled js file for you.

CSS and SASS

For processing CSS/SASS through webpack it first loads the css first into the js file then it is extracted from it and emmit it into a file. To use SASS, we configure webpack to transpile SASS to CSS and then performs the above mentioned procedure. The list of plugins we need are :

  • node-sass : Provides binding for Node.js to LibSass. The sass-loader requires node-sass and webpack as peerDependency. Thus you are able to control the versions accurately.

  • sass-loader : Loads a SASS/SCSS file and compiles it to CSS

  • css-loader : Interprets @import and url() like import/require() and will resolve them.

  • extract-text-webpack-plugin : Extract text from a bundle, or bundles, into a separate file.

  • style-loader : Loads the CSS file into the html document with the help of style tag.

    *Note Before using node-sass ensure the SASS command line tool is installed in your system.*

Now we install the dependencies :

1npm i -D sass-loader node-sass css-loader style-loader extract-text-webpack-plugin@next

First we import extract-text-webpack-plugin and then instantiate it with the file output path.

1// ....
2const ExtractTextPlugin = require('extract-text-webpack-plugin');
3
4const ExtractCSSPlugin = new ExtractTextPlugin({
5 filename: './assets/css/index.css'
6});

Now let’s configure webpack to use CSS and SASS loader :

1const config = {
2 // ...
3 module: {
4 // ...
5 {
6 test: /\.scss$/,
7 include: [path.resolve(__dirname, '../src', 'assets', 'scss')],
8 use: ExtractCSSPlugin.extract({
9 use: [
10 {
11 loader:'css-loader',
12 options: {
13 minimize : true
14 }
15 },
16 {
17 loader:'sass-loader',
18 options: {}
19 }
20 ],
21 fallback: 'style-loader'
22 })
23 },
24 },
25 plugins: [
26 // ...
27 ExtractCSSPlugin
28 ]
29}

Now that needs some explanation.

  • test and include : That should be obvious.

  • use : Here we are calling ExtractCSSPlugin.extract function with a configuration object as parameter.

  • First we define an array called use inside it which will hold configurations for the loaders we’ll be using.

  • If you notice the CSS loader has an option minimize set to true. this minifies the generated CSS file.

  • The next config object is the sass-loader

  • fallback : Loader that should be used when the CSS is not extracted(i.e. in an additional chunk when allChunks: false).

  • We also have added the ExtractCSSPlugin to the plugins array.

Now let’s test this. Add the following style to /assets/scss/app.scss :

1$bgcolor: #a80c0c;
2body {
3 background: $bgcolor;
4}

For webpack to recognise the SASS file we have to import it in our app.js :

1require('../scss/app.scss');

Now let’s test if everything is working. Open command line and run npm run build. Now if you open `/dist/assets/css/app.css’, you should be able to see :

1body{background:#a80c0c}

Loading images

Currently our config can bundle all files except images. For bundling images we use file-loader. First we install it:

1npm i -D file-loader

The config for loading images :

1{
2 test: /\.(jpeg|jpg|png|gif|svg)$/,
3 use: [{
4 loader: 'file-loader',
5 options: {
6 name: '[name].[ext]',
7 outputPath: './assets/media/',
8 }
9 }]
10}

The output path must end with / else the while saving the files, the output filename will be appended to the output path rather than saving it inside the folder.

More details can be found here.

Now let’s add images, The images should be saved inside /src/assets/media/. We also have to import the image in the HTML file. According to our webpack config the files will be saved in assets/media so we’ll reference that path in our HTML file :

1<img src="./assets/media/image.jpeg" alt="">

let’s test the config, run npm run start from the command line. The browser should open up showing a red page with the image. Here’s the configuration we wrote till now :

webpack.common.js

1const path = require('path')
2const CleanWebpackPlugin = require('clean-webpack-plugin');
3const HtmlWebpackPlugin = require('html-webpack-plugin');
4const ExtractTextPlugin = require('extract-text-webpack-plugin');
5const ExtractCSSPlugin = new ExtractTextPlugin({
6 filename: './assets/css/[name].css',
7});
8const config = {
9 context: path.resolve(__dirname, "../src"),
10 entry: {
11 app: './assets/js/app.js'
12 },
13 output: {
14 path: path.resolve(__dirname, '../dist'),
15 filename: 'assets/js/[name].bundle.js'
16 },
17 module: {
18 rules: [{
19 test: /\.js$/,
20 exclude: /node_modules/,
21 use: ["babel-loader"]
22 },
23 {
24 test: /\.(html)$/,
25 use: {
26 loader: 'html-loader'
27 }
28 },
29 {
30 test: /\.scss$/,
31 include: [path.resolve(__dirname, '../src', 'assets', 'scss')],
32 use: ExtractCSSPlugin.extract({
33 use: [{
34 loader: 'css-loader',
35 options: {
36 minimize: true
37 }
38 },
39 {
40 loader: 'sass-loader',
41 options: {
42 }
43 }
44 ],
45 fallback: 'style-loader'
46 })
47 },
48 {
49 test: /\.(jpeg|jpg|png|gif|svg)$/,
50 use: [{
51 loader: 'file-loader',
52 options: {
53 name: '[name].[ext]',
54 outputPath: './assets/media/',
55 }
56 }]
57 }
58 ]
59 },
60 plugins: [
61 new CleanWebpackPlugin(['dist']),
62 new HtmlWebpackPlugin({
63 filename: 'index.html',
64 template: 'index.html'
65 }),
66 ExtractCSSPlugin
67 ]
68};
69module.exports = config;

The complete repository can be viewed here.

Thats all for this post. In the next post we’ll see configuring webpack for production and a powerful feature of webpack, code splitting in webpack.