webpack

webpack is a build system for JavaScript which requires node to run. It refers to itself as a static module bundler. It views your source files as modules and organizes modules and their dependencies inside a graph. It will output one or more bundles after traversing the graph. So modules in a dependency graph in, bundles out.

webpack uses one entry point which is similar to a main() function in a programming language as it marks the starting point of operation. The entry point, entry for short, is the module where webpack starts to traverse the dependency graph.

Loaders do Load Modules

In webpack, you import modules to build up the dependency graph. A module can be any file, as long as there is a loader for that type of file. By default, webpack understands JavaScript and JSON files and it can convert those into modules and add them to the dependency graphs via import statements.

Additional loaders allow webpack to understand other types of files, convert them into modules and add them into the dependency graph.

When webpack sees an import, it looks into it’s definitions of loaders and if it finds a matching loader, it applies that loader to the import. The module rules for loader definitions are contained in the webpack.config.js.

const path = require('path');

module.exports = {
  output: {
    filename: 'my-first-webpack.bundle.js',
  },
  module: {
    rules: [{ test: /\.txt$/, use: 'raw-loader' }],
  },
};

In the rule above, test defines which import regular expression the loader will match and the use-part defines the loader implementation to use when the regex matches. In this example, the raw-loader will be applied to all imported .txt files.

Ultimately, your resulting bundle or bundles will contain all the JavaScript, HTML, CSS, images and other files that you import as modules. That’s right, you treat CSS files and everything you need as a module when using webpack. You will in fact import CSS files! Pretty exciting concept if you ask me!

The Entry

webpack uses a configuration file webpack.config.js. Here you specify the entry:

module.exports = {  
entry: './path/to/my/entry/file.js',
};

The entry will be a file called ./src/index.js in most cases.

Plugins

Plugins are added via the webpack.config.js file. They are then called by the webpack compiler during compilation.

A example configuration is

const HtmlWebpackPlugin = require('html-webpack-plugin'); //installed via npm
const webpack = require('webpack'); //to access built-in plugins
const path = require('path');

module.exports = {
  entry: './path/to/my/entry/file.js',
  output: {
    filename: 'my-first-webpack.bundle.js',
    path: path.resolve(__dirname, 'dist'),
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        use: 'babel-loader',
      },
    ],
  },
  plugins: [
    new webpack.ProgressPlugin(),
    new HtmlWebpackPlugin({ template: './src/index.html' }),
  ],
};

here, the HTMLWebpackPlugin is used.

Example

First, create a node project.

cd dev/javascript
mkdir webpack_helloworld
cd webpack_helloworld
npm init -y
code .

Then install the dependencies.

npm install --save-dev webpack webpack-cli
npm install --save-dev html-webpack-plugin

Setup the files and folders.

insert a webpack.config.js next to the package.json.

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

const path = require('path');module.exports = {
mode: 'development',
entry: './index.js',
output: {
path: path.resolve(__dirname, './dist'),
filename: 'index_bundle.js',
},
plugins: [new HtmlWebpackPlugin()],
};

This webpack.config.js requires a index.js file, so you have to create one next to the webpack.config.js. Inside the index.js file, just output some text.

console.log('webpack works! - Hello World!');
alert('webpack works! - Hello World!');

To start the webpack build, add a script in the package.json

"scripts": {    
"build": "webpack --config webpack.config.js",
"test": "echo \"Error: no test specified\" && exit 1"
}

Start the webpack compilation

npm run build

Now check your project folder. There is a dist folder generated for you. Inside that dist folder, the HtmlWebpackPlugin has generated a index.html file that imports a generated index_bundle.js. The index_bundle.js file contains all entry points and their dependencies defined in webpack.config.js. That means it will contain the code from index.js in this example.

Revisiting the Result

Now, webpack created a dist/index.html file for us and it bundle all entry modules (currently javascript files) and all their dependencies and imported them automatically into the dist/index.html file.

This is a wonderful situation for building a framework that generates all HTML markup programmatically. You could build all HTML via JavaScript’s DOM API from your javascript entry point. This is not necessarily what you want. If you want to use webpack and it’s hot reload feature to work with CSS and HTML, then you most likely want a HTML file you have full control over.

Using HTML with webpack

The question is, how do you add your own html markup into the generated index.html? The answer to this question is the HtmlWebpackPlugin’s template feature. Credits go to this solution: https://stackoverflow.com/questions/39798095/multiple-html-files-using-webpack/63385300 and the documentation https://github.com/jantimon/html-webpack-plugin/blob/main/docs/template-option.md

A template is a HTML file that you put into a src folder and that you configure in the HtmlWebpackPlugin in webpack.config.js:

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

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

A valid template (src/index.html) looks like this:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Webpack App 2</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>
<h1>Hewlo Wurl!</h1>
</body>
</html>

Webpack will first copy this file into the dist folder and then webpack will modify the copied file. Webpack will insert a script-tag into the header and that way it will import the compiled modules (bundles) into the html file copied from the template.

To try this out, build again (npm run build) and reopen the dist/index.html in your browser. You should first get an alert box which is contained in the entry module index.js (if you followed this example) and then you should see the custom HTML from the index template! Wonderfull, we are almost there!

The next question is, how to import CSS.

Using CSS with webpack

Let’s say you want to use ./src/index.css

h1 {
color: red;
}

First install the webpack css-loader and the style-loader

npm install --save-dev css-loader
npm install --save-dev style-loader

Next add a rule into webpack.config.js that makes webpack apply the css plugin to css files:

const HtmlWebpackPlugin = require("html-webpack-plugin");
const path = require("path");

module.exports = {
mode: "development",
entry: "./index.js",
output: {
path: path.resolve(__dirname, "./dist"),
filename: "index_bundle.js",
},
plugins: [
new HtmlWebpackPlugin({
filename: "index.html",
template: "src/index.html",
chunks: ["main"],
}),
],
module: {
rules: [
{
test: /\.css$/i,
use: ["style-loader", "css-loader"],
},
],
},
};

No this is where it gets a little weird, at least to my liking. You will use an import statement to import your css file into the entry. The import will make webpack apply the css-loader and style-loader to the css file.

import css from "./src/index.css";

console.log('webpack works!');
alert('test');

Now rebuild and reload the generated index.html. The header should be displayed in red color.

Using JSON files with WebPack

Say you have a JSON file on your hard drive that contains JSON data you need process in some JavaScript routines. You can import the JSON file using a Promise and process the data once the Promise is successfull.

import(
'./data/testdata.json'
).then(({default: testdata}) => {

let jsonOutputElement = document.getElementById("rawJson");
jsonOutputElement.innerHTML = JSON.stringify(testdata, undefined, 2);

// do whatever you like with your "jsonMenu" variable
//console.log('testdata: ', testdata);
initTreeStructure(testdata);
});

For webpack to load the JSON file, it uses a JSON-Loader, that not only reads the file to a string but it parses the JSON to a JavaScript object.

In earlier versions of webpack, a json loader had to be installed manually and added to the list of loaders in webpack.config.js

npm install json-loader --save-dev
module.exports = {
module: {
loaders: [
{
test: /.json$/,
loader: 'json-loader'
}
]
}
}

It seems that manually adding a JSON-loader in newer versions of webpack actually causes issues because the added JSON-loader conflicts with the onboard JSON-loader leading to parse errors during JSON parsing! I found that in the current version of webpack, it is sufficient to just import JSON files without installing and configuring any JSON-loader.

Using HTML components with WebPack

https://medium.com/hackernoon/using-html-components-with-webpack-f383797a5ca

Hot Module Replacement aka. Hot Reload

webpack can be instructed to watch your files for changes, compile and reload the page in the browser for you. That way the latest changes are available on save.

To enable hot reload, first install webpack-dev-server

npm install webpack-dev-server --save-dev

Now, edit package.json and add a serve script:

"scripts": {
"build": "webpack --config webpack.config.js",
"serve": "webpack serve",
"test": "echo \"Error: no test specified\" && exit 1"
},

Run the serve script

npm run serve

This will bring up a webpack development server with hot load capabilities. In the console, a URL is printed. You have to open that URL using a browser to get the hot loaded page. Do not just open the html page in your dist folder, this file will not be hotloaded. Your browser has to access the webpage from the hot loaded server!

Test it out, change your css or your JavaScript. The browser is instructed to reload the page and your changes are immediately reflected in the browser.

Leave a Reply