16 Aug 2022 ~ 8 min read

Webpack, is it really legacy - Part 01

Webpack is one of the older build tools out there, but is it that bad? let's see

Listen to something with me 😍

Want to listen somewhere else? 🎧

Table of Contents

Preface

Webpack is a great tool, but in JS land something new is always around the corner. Today it’s this, tomorrow it’s that… bun intended. well what do you choose? do you go for the flashy new thing? or do you stick with what you’ve already known for months. Well, it depends. If you’re just starting to use something then I suggest you try what ever sounds good to you and try it out. If it works out well, no need to go hopping on different tools. Use whatever works for you.

But for me I’ve felt comfortable with webpack, it was hard in the beginning like everything else. So here is a series of articles to let you know how I utilize webpack on my projects.

However, this series of articles is not about convincing you webpack is better than the other articles. But it’s just me sharing my build process using webpack. Like I said previously, what works is probably the best option.

Top

Introducing Webpack

If you’ve never heard of webpack here is a simple explanation, webpack is a tool that helps with bundling your project, converting your project from one file to another (i.e. TypeScript to JavaScript), code-splitting and much more. Here are some of the characteristics of webpack

  1. Composable, so you can compose configurations to make custom configurations
  2. Extensible, you can extend your configuration using community built or official extensions called plugins.
  3. Has builtin optimizations that get used depending on different build modes, production, development or none to totally avoid them.
  4. Webpack runs on NodeJS, specifically node 10.13 and above.

Let’s begin with a project, I’ll setup a simple webpack project and show you how I can customize to your hearts content. This project will be a simple vanilla JavaScript Project, a simple web-app.

Top

Setting Up

So, let’s setup a simple project. Initialize an npm project and setup our basic directory structure.

# Not the IDE, although WebStorm would be perfect for this project 😉
mkdir webstorm && cd webstorm/
npm init -y # Modify the template package.json to your liking

Side note, I’m calling it web-storm because it’s a web app for brainstorming.

Folder structure

For the directory structure, I’m just simply going to put the entry point in src and continue adding other code in there. Configs can be inside config, but for now we’ll just have the webpack config file in the root of the project.

webstorm
   |- configs # empty for now
   |- dist
      |- index.html
   |- src
      |- index.js
   |- .gitignore
   |- package.json
   |- webpack.config.js

I am also using pnpm because I think it’s noticeably faster than npm. So if you feel like checking it out here you go.

Now let’s add an main html and index.js files

# Create a file inside dist and
# populate it with a basic html body
mkdir dist && touch dist/index.html
# creating entry javascript file
mkdir src && touch src/index.js

I added this to it to the html and index.js files,

<!-- dist/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>Webstorm</title>
</head>
<body>
    Hello, World 🙃
</body>
</html>
// src/index.js
console.log("Hello World");

Top

Configuring Webpack

Now, let’s configure webpack. It’s going to be used to build the file and place it in the output directory.

npm install -D webpack webpack-cli
touch webpack.config.js

Now, let’s add the configuration for webpack inside the webpack.config.js file.

// webpack.config.js
const path = require("path");

module.exports = {
    entry: "./src/index.js",
    mode: "development",
    output: {
        filename: 'main.js',
        path: path.resolve(__dirname, "dist")
    }
}

Finally let’s add a build command to our package.json file. Add a script like so to the scripts section of package json.

{
   // top rest do not modify...
   scripts: {
      build: "webpack --config webpack.config.js"
   },
   // bottom rest do not modify...
}

Finally include the output script inside of your html file.

<!-- dist/index.html -->
<!-- top rest leave as is -->
<body>
    Hello, World 🙃
    <script src="main.js"></script>
</body>
<!-- bottom rest leave as is -->

Initial build of the project

Go ahead and build the project, that should create a main.js file inside dist directory. Now you can build the project to check if it outputs a console.log on the page. execute

npm run build
# If you don't have a live plugin/program you can use python3
cd dist
python3 -m http.server 8080

Resulting output, up-to now should be like this.

first-project-build

Top

Stepping up our project

Let’s go ahead and customize our webpack config even more. So next our goal is going to be

  1. Setting up a dev server for our project, so it can get served and updated automatically as we work on it.
  2. Making sure our dev server is going to be using our template html file too
  3. We’re going to be reading for PORT env variable from the Operating System, so if that is setup then we’ll replace the default port 8080 with it.

We’re going to be using webpack-dev-server and html-webpack-plugin for this. So let’s go ahead and do it.

Configuring webpack-dev-server

Move the index.html file in dist to public directory. Move the webpack.config.js to config too.

mkdir public && mv dist/index.html public/

Let’s install and configure webpack-dev-server. Modify the webpack config like so

npm install -D webpack-dev-server html-webpack-plugin
// config/webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin")

const rootDir = path.resolve(__dirname, "..")
const PORT = process.env.PORT || 8080;

module.exports = {
    // top rest, leave unchanged
    devServer: {
      port: PORT,
      host: "0.0.0.0",
      watchFiles: ["src/**/*", "public/**/*"],
    },
    plugins: [
      new HtmlWebpackPlugin({
        template: path.resolve(rootDir, "public", "index.html"),
        inject: "body",
      }),
    ],
}

Let’s explain what this is doing now,

  1. devServer: here we are configuring the dev server
    • We configure port and host
    • If port is set in the as PORT however, we use that specific port
    • Set up host so that we can use the dev server from another device on the network.
  2. plugins: Tells webpack to use the html-webpack-plugin, and set the template html to use, and inject or add any compiled files to the end of body tag in the final index.html output.

Now, you can remove the script tag inside public/index.html for loading main.js. Because webpack is going to automatically add it to your final output.

Top

Adding Features

Now let’s add feature specific things, the goal here is to have an app that saves when we tell it in markdown but would not save it to a server later. It’s just brainstorming on the web… see what I did there. So it’s just a simple webapp without a backend, let’s list what we’ll do here.

  1. Install Showdown.js for markdown to html conversion
  2. Use it to take input from an input-tag and add a html tag with the contents of the input.
# Installing the plugin
npm install -S showdown

Html & JavaScript

<!-- top rest, leave as is -->
<body style="font-family: helvetia, arial;">
   <fieldset>
      <label for="idea">Tell Me Your Idea!</label>
      <br><br>
      <textarea name="idea" id="idea"
         cols="30" rows="15">Start Writing...</textarea>
   </fieldset>
   <br>

   <h4>Brainstormed Ideas</h4>
   <hr>
   <div class="brainstormed"></div>
</body>

And finally add these into your index.js file. Since this is not a JavaScript article I’ll simply provide a brief description of what it does.

const showdown = require("showdown");
const converter = new showdown.Converter();

const onReadyListener = () => {
  /** @type {HTMLTextAreaElement} */
  const ideaElem = document.getElementById("idea");
  const brainstormedElem = document.getElementById("brainstormed");
  let shiftModPressed = false;
  
  if (!ideaElem) return;
  if (!brainstormedElem) return;

  ideaElem.addEventListener("keydown", (e) => {
    if (e.key !== "Shift") return;
    if (e.repeat) return;

    shiftModPressed = true;
  })

  ideaElem.addEventListener("keyup", (e) => {
    if (e.key !== "Shift") return;
    shiftModPressed = false;
  })

  ideaElem.addEventListener("keydown", (e) => {
    if (e.key !== "Enter") return;
    if (!shiftModPressed) return;
    
    const ideaText = ideaElem.value;
    const ideaHtml = converter.makeHtml(ideaText);

    if (brainstormedElem.children.length > 0)
      brainstormedElem.prepend(document.createElement("hr"));

    const newIdeaElem = document.createElement("div");
    newIdeaElem.innerHTML = ideaHtml;
    brainstormedElem.prepend(newIdeaElem);

    e.preventDefault();
  })
}

document.addEventListener('DOMContentLoaded', onReadyListener);

Briefly, what this does is this. When you press on Shift+Enter the content you wrote inside the textarea will be converted to HTML and added as a div element on top(prepend) of the Brainstormed ideas block children.

Results

Final result after being done with this article

Top

Conclusion

I believe this is enough for this part of the article, however this might seem very ugly for now. For that we will continue on the webpack journey and add some scss support, add image assets and stuff like that but that is for the next article.

If you want to follow this article but missed something, here is the repo for the article go ahead and take a look.

References


Headshot of Maxi Ferreira

Hi, I'm Zablon. I'm a software engineer, mostly working in the web space, based in Ethiopia. You can follow me on Twitter, see some of my work on GitHub, or read more about Qebero.dev on here.