Where to Discuss?

Preface

Goal: Bundle Pug + Stylus + Coffeescript together, using webpack.

While the previous grunt article target is conservative web development, now comes the time for webpack targeting modern web development.

Source Examples

You can obtain source examples here:

![Webpack: Nerdtree configuration][41-nerdtree-webpack]


Example: In a Nutshell

Webpack is a bundler.

How does it looks like?

Supposed that you have these source assets:

├── css
│   └── style.styl
├── js
│   └── script.coffee
└── pug
    ├── alert.pug
    └── partials
        └── body.pug

All the sources, including pug, stylus, and coffeescript, will be bundled into one bundle.js and one index.html.

└── dist
    ├── bundle.js
    └── index.html

It means, the stylesheet also included, as a part of javascript bundle.js.

I hope this explains 🙏🏽.

Official Documentation

You should read this first.

This article is useless if you never touch the official documentation.

Credits

I’m not an expert on webpack. In fact I don’t get what webpack is all about, after reading the good official documentation. One day, my friend in a local group gave me, a short example of webpack configuration, that turn the lights on my brain. So, thank you, Alfian Hidayat. You make these webpack article happened.

Learn by Example

If in the past we learn that, we can put assets as inline, internal, or external file. With webpack we can put assets as bundle. It means we can put stylesheet in javascript, along with all other js script.

Just like grunt, webpack can translate template, such as pug to html, stylus to css, coffeescript to javascript and so on.

Webpack as a bundler is more than just template preprocessor. Rendering webpack is useful, when it comes to SPA (Single Page Application). But for a starter, this article only cover this preprocessor stuff. Any beginner can dive deeper later to enhance their knowledge.

This article use previous example, converted to webpack configuration, explained step by step.


1: Preparation

For each step, we are going to need these

  1. package.json, and

  2. webpack.config.js

  3. Assets in respective directory, such as js, css and so on.

  4. Output directory, in this example is dist.

package.json

All you need to know is each step has different devDependencies.

  "devDependencies": {
    "webpack": "^4.43.0",
    "webpack-cli": "^3.3.11"
  },

To begin with each step, you need to run npm install first.

Webpack: Prepare: NPM Install

Directory Tree

Our preparation example only have to files.

  • js/script.js with the same content with our previous article.

  • dist/bundle.js generated by webpack later. You can remove dist/bundle.js anytime.

$ tree
.
├── dist
│   └── bundle.js
├── js
│   └── script.js
├── package.json
└── webpack.config.js

2 directories, 4 files

Webpack: Prepare: Directory Tree

Th example script.js contain this file:

document.addEventListener("DOMContentLoaded", function(event) { 
  var alertButtons = document.getElementsByClassName("dismissable");
    
  for (var i=0; i < alertButtons.length; i++) {
    alertButtons[i].onclick = function() {
      this.parentElement.style.display='none';
      console.log('Close Button. Element ' + this.parentElement.id + ' dismissed');
      return false;
    };
  };
});

webpack.config.js

A typical configuration is shown as below.

const path = require('path');

module.exports = {
  mode: 'development',
  entry: {
    bundle: [
      "./js/script.js"
    ],
  },
  output: {
    filename: "[name].js",
    path: path.resolve(__dirname, 'dist'),
  }
};

Now you can run webpack in your favorite terminal.

$ webpack
Hash: 7c1326f6ac166c3d0b5f
Version: webpack 4.43.0
Time: 126ms
Built at: 05/09/2020 12:27:01 AM
    Asset      Size  Chunks             Chunk Names
bundle.js  4.62 KiB  bundle  [emitted]  bundle
Entrypoint bundle = bundle.js
[0] multi ./js/script.js 28 bytes {bundle} [built]
[./js/script.js] 515 bytes {bundle} [built]

Webpack: Prepare: Running Webpack in Terminal

As you can see, the webpack generate bundle.js, that contain long lines, and also this lines somewhere.

eval("(function() {\n  document.addEventListener('DOMContentLoaded', function(event) ...

I think that’s enough about preparation.


2: Assets - Bundle

Consider start with example.

Objective

Bundle both the css/style.css and js/script.js stylesheet into dist/bundle.js

Directory Tree

Additional file: css/style.css.

.
├── css
│   └── style.css
├── dist
│   ├── bundle.js
│   └── example.html
├── js
│   ├── live.js
│   └── script.js
├── package.json
└── webpack.config.js

HTML View

I create dist/example.html manually, so you examine the bundle result in page.

I also utilize live.js so the page refreshed automatically, while using simple web server such as browsersync.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Example Webpack</title>
    <script src="./bundle.js"></script>
    <script src="../js/live.js"></script>
  </head>
  <body>
    ...
  </body>
</html>

Stylesheet

...
.red {
  color: #fff !important;
  background-color: #f44336 !important;
}
...

package.json

Additional node modules css-loader and style-loader.

  "devDependencies": {
    "webpack": "^4.43.0",
    "webpack-cli": "^3.3.11",
    "css-loader": "^3.5.3",
    "style-loader": "^1.2.1"
  },

webpack.config.js

The configuration has one entry point: the bundle.

const path = require('path');

module.exports = {
  mode: 'development',
  entry: {
    bundle: [
      "./js/script.js",
      "./css/style.css",
    ],
  },
  output: {
    filename: "[name].js",
    path: path.resolve(__dirname, 'dist'),
  },
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: ['style-loader', 'css-loader'],
      },
    ],
  },
};

Command Line Interface

Just run the webpack in terminal

$ webpack
Hash: b13fabe1718a323be83c
Version: webpack 4.43.0
Time: 1243ms
Built at: 05/09/2020 12:49:18 AM
    Asset      Size  Chunks             Chunk Names
bundle.js  18.1 KiB  bundle  [emitted]  bundle
Entrypoint bundle = bundle.js
[0] multi ./js/script.js ./css/style.css 40 bytes {bundle} [built]
[./css/style.css] 519 bytes {bundle} [built]
[./js/script.js] 515 bytes {bundle} [built]
[./node_modules/css-loader/dist/cjs.js!./css/style.css] 1.44 KiB {bundle} [built]
    + 2 hidden modules

View in Browser

Run your browsersync or just open the example.html from file manager. You will see something similar like this below:

Webpack: Bundle: Open example.html in Browser

Have a look at the source code, the stylesheet is written inside javascript instead of CSS file.

Entry Point in Configuration

The webpack configuration is pretty self-explanatory. Of course, with assumption that the audience of this article, has ability to read official documentation.

The entry point, has been altered into this:

  entry: {
    bundle: [
      "./js/script.js",
      "./css/style.css",
    ],
  },

Instead of the previous one below.

  entry: {
    bundle: [
      "./js/script.js"
    ],
  },

Loader in Configuration

Also you need rules in modules that use both style-loader and css-loader.

  module: {
    rules: [
      {
        test: /\.css$/i,
        use: ['style-loader', 'css-loader'],
      },
    ],
  },

3: Assets - Split

Objective

Split css/style.css into dist/style.js

Directory Tree

Additional output: dist/style.js.

.
├── css
│   └── style.css
├── dist
│   ├── bundle.js
│   ├── example.html
│   └── style.js
├── js
│   ├── live.js
│   └── script.js
├── package.json
└── webpack.config.js

The webpack should generate two files:

  • dist/bundle.js, and
  • dist/style.js, and

HTML View

I add style.js in header of dist/example.html manually, so that the page have stylesheet.

  <head>
    <meta charset="utf-8">
    <title>Example Webpack</title>
    <script src="./bundle.js"></script>
    <script src="./style.js"></script>
    <script src="../js/live.js"></script>
  </head>

package.json

Still the same package.json. with these two css-loader and style-loader.

  "devDependencies": {
    "webpack": "^4.43.0",
    "webpack-cli": "^3.3.11",
    "css-loader": "^3.5.3",
    "style-loader": "^1.2.1"
  },

webpack.config.js

The configuration has two entry point: bundle and style.

const path = require('path');

module.exports = {
  mode: 'development',
  entry: {
    bundle: "./js/script.js",
    style: "./css/style.css"
  },
  output: {
    filename: "[name].js",
    path: path.resolve(__dirname, 'dist'),
  },
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: ['style-loader', 'css-loader'],
      },
    ],
  }
};

Command Line Interface

Just run the webpack in terminal

❯ webpack
Hash: 2d794c3b85947839df0a
Version: webpack 4.43.0
Time: 1332ms
Built at: 05/09/2020 1:04:40 AM
    Asset      Size  Chunks             Chunk Names
bundle.js  4.29 KiB  bundle  [emitted]  bundle
 style.js  16.9 KiB   style  [emitted]  style
Entrypoint bundle = bundle.js
Entrypoint style = style.js
[./css/style.css] 519 bytes {style} [built]
[./js/script.js] 515 bytes {bundle} [built]
[./node_modules/css-loader/dist/cjs.js!./css/style.css] 1.44 KiB {style} [built]
    + 2 hidden modules

As you can see, the webpack generate two js files.

View in Browser

If evertyhing goes well, the result in browser should show similar looks, and the dismissable button should works too.

Entry Point in Configuration

The entry point, has been altered into this:

  entry: {
    bundle: "./js/script.js",
    style: "./css/style.css"
  },

Instead of the previous one below.

  entry: {
    bundle: [
      "./js/script.js",
      "./css/style.css",
    ],
  },

That is all.


What’s Next?

Consider continue reading [ Bundler - Webpack - Part Two ].