17, Sep 2022 ~ 7 min read

Webpack, is it really legacy - Part 03

Understanding and Managing your project build output.

Listen to something with me 😍

Want to listen somewhere else? 🎧

Table of Contents

Introduction

On this 3rd article on the Webpack, is it really legacy series, I will be going through the process of building your applications code for a certain module system. I’ve already covered the topic of modules in JavaScript’s ecosystem here, so make sure to check it out if you’re not familiar with the concept of modules.

For this part of the series I will use another code sample. But this articles code sample and the previous articles code will be relevant in the upcoming articles, just keep that in mind.

Top

Refresher on Modules in the JavaScript ecosystem

Modular programming is a software design technique that emphasizes separating the functionality of a program into independent, interchangeable modules, such that each contains everything necessary to execute only one aspect of the desired functionality. Wikipedia

A Module is a piece of code that is separated in a single file. Modules are used to break apart your codebase so it doesn’t have one huge file containing everything, so it becomes easier to maintain, understand and test. In JS Land, modules came at a later point after the creation of modules.

In a typical JavaScript fashion, every implementation of the JavaScript Interpreter had it’s own approach to modules. NodeJS came up with the CommonJS module system, browsers by default used Globals and script tags, and after a while ES6 was standardized. With ES6 standardization the ES6 Module system came to exist which most people are familiar with when using something like TypeScript. Also let’s not forget the community based library systems like AMD (Asynchronous Module Definitions) and UMD (Universal Module Definition).

Top

Building for a target system

Now that you understand what the module system is, you as a developer are faced with building your library for a target module system now. At least until everything is cleared out with a common standard for all JavaScript Interpreters out there, if they decide to be up to date that is.

Webpack has options to build your application or library targeting a specific module system or multiple if you’d like. One aspect of using a build tool in the JavaScript ecosystem is to build to a certain module type, so we’ll take a look at how we’ll do that using Webpack.

Top

A simple Markdown parser library

In this article, I want to create a simple markdown to HTML converter, this simple library converts the markdown text into HTML but with added configuration to that library. Later I want to be able to reuse this piece of code in a http-server but I don’t want to copy and paste a bunch of code just to do that.

The plan for this library we’re creating is to have the following things already configured for Showdown.

  1. Enable Emoji
  2. Should parse Metadata
    • Title
    • Category
    • Tags
    • Id
  3. Header Level Start should be set to Header Level 3
  4. Open Links in new windows by default
  5. Enable Strike Through
  6. Enable Simple Line Breaks

Top

Here the code we’ll be using

Since this is not a JavaScript article I will simply put the code, as I did in the previous articles. You can use and modify the code as you like.

const showdown = require("showdown");

const OPTIONS = {
    emoji: true,
    metadata: true,
    stikethrough: true,
    underline: true,
    headerLevelStart: 3,
};

const Mdlib = (options = {}) => {
    const _converter = new showdown.Converter({
        ...OPTIONS, // default options,
        ...options // override default options
    });

    const makeError = (missingProperties) => {
        return Error("Metadata doesn't contain valid metadata properties", {
            metadataProperties: missingProperties,
        })
    };

    return {
        _converter,
        makeHtml: (mdString) => {
            const html = _converter.makeHtml(mdString);
            const metadata = _converter.getMetadata();
            const expectedMetadata = ['title', 'category', 'tags', 'id'];

            if (typeof metadata !== "object")
                throw makeError(expectedMetadata);

            // Filter missing metadata
            const missingMetadata
                = expectedMetadata.filter(
                    (metadataKey) =>
                        !metadata.hasOwnProperty(metadataKey)
                );

            if (missingMetadata.length > 0)
                throw makeError(missingMetadata);

            // Convert a string of tags to an array
            metadata['tags'] = metadata
                .tags.split(",")
                .map(tag => tag.trim());

            return {
                html,
                metadata,
            }
        },
    };
};

module.exports = Mdlib;

Disclaimer: you can always take a look at the Github repo. To clarify, I’ll be assuming you’ve already have NodeJS setup and already know we’re using ShowdownJS from our previous articles in the series.

Top

Which Module System to Target?!

I’ve previously went through JavaScript’s Module System, and right now we want to have a library that is going to be used in another project, so we should decide on the type of module we are targeting for the library final’s bundle build. There are a couple of approaches we can take

  1. We can build separate packages targeting different module systems and use each in their environment.
  2. We can build targeting a single module system, and require usage only through that specific environment.
  3. We can build output targeting all/most module systems all at once.

I prefer to build targeting all of the environments I am aiming at if possible, the browser and NodeJS. Naturally the clear output types to use to target both is the UMD module system. It is intended to have an output that is supported in both browsers and NodeJS, so we’ll tell Webpack to output a bundle of that type.

Top

Configuring Project Output

To output a certain type of module, we can configure Webpack to build a certain bundle type and have a certain object available for browsers. We can configure our build like so,

const config = {
    entry: "./src/index.js",
    mode: "development",
    output: {
        filename: "mdlib.js",
        path: path.resolve(rootDir, "dist"),
        clean: true,
        libraryTarget: 'umd',
        library: "mdlib",
    },
    optimization: {
        concatenateModules: true,
    }
};

Let’s breakdown the output configuration,

  1. filename: The name of the resulting file from the build, in this case a single file named mdlib.js.
  2. path: The path of the bundle file, in this case into a distribution directory in our rootDir
  3. clean: Tells Webpack to clean/remove any artifacts from the previous build
  4. libraryTarget: The type of output we are building, in this case a UMD type bundle
  5. library: A string, used to call the default export of the entrypoint of our project with. So in this case we can go mdlib and access the default export from src/index.js file globally in the browser.

Top

Wrapping Up

This is a simple explanation on how our package, mdlib, is configured to be used as a library inside another project. We will continue on working another article using this library. We will showcase how someone might build something entirely using Webpack.

To bring the previous articles up to now together, we have done a couple of things on the previous parts of the Webpack, is it really legacy series.

  • Part 01: What Webpack is and a basic use case.
  • Part 02: How Webpack is used to build a basic Single Page Application
  • Part 03: Configuring Webpack to build an output bundle, for a library.

In the next part, we will see how you can use it in a monorepo project. We’ll pair it with another tool to manage our project build sequence, and use Webpack as our build platform for all other projects.

Top

References

Top


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.