In our last installment, Jay Ess was able to solve his problems when he discovered bundlers! In our final installment, he examines Browserify and webpack to see how bundlers actually work. 

Part 3: Holy Tools

Chapter VIII: Generated Code

It’s a beautiful morning. The sun is shining, and Jay Ess's work is progressing smoothly. But still, he feels restless. Why? He's discovered so many bundlers that solve the problems that have haunted him for so long, but he doesn’t know how they work. After a long sip of coffee, he cries, “this is witchcraft, man! How does it work?!”

He has to start somewhere, and like a chemist picking a sample for research, he decides to choose Browserify since it's a simpler tool. (Keep in mind that every bundler works similarly and produces similar results. The idea here is not to examine code in detail; the idea is to have an idea.)

Suppose one of Jay Ess’s files (something called LoaderLink.js) has the following code:

const XHR = require ('../ lib / xhr.js');

const DOM = require ('../ lib / domUtils.js');

function loader {container} {

    const output = DOM.printTo (container);

    XHR.get (href, content => {

        let {content, js} = DOM.parse (content);

        // ...

    });

}

module.exports = loader;

(Most of the code was removed from the example, but the interesting part on importing and exporting modules is still here.)

So Jay Ess sits in his chair, installs Browserify, and runs it on his example code. He waits and stares at his screen like a dog watching her food being prepared and baptized with a little meat sauce. Actually, he doesn’t, because the process is extremely fast. The result? A shiny, bundled file that he immediately opens to investigate. Here’s the (very simplified excerpt of) code file:

{

    1: ...,

    2:[

            function (require, module, exports) {

                const XHR = require ('../ lib / xhr.js');

                const DOM = require ('../ lib / domUtils.js');

                function loader {container} {

                    const output = DOM.printTo (container);

                    XHR.get (href, content => {

                        var {content, js} = DOM.parse (content);

                    // ...

                    });

                }

                module.exports = loader;

            },

            {"../lib/domUtils.js":4,"../lib/fnbasics.js":5,"../lib/xhr.js":6}

    ],

    3: ...

}

“Holy tools, Batman, this is weird!” he cries. “So this is thrown at an object, which is passed to some embracing function, which will execute each of the modules/function expressions. But wait a minute… this transformation basically just embraces the original script code and extracts the dependencies that each module requires in that code, reading each line where there is a require and putting all the names in a collection of dependencies to later manage these modules correctly…”

At this point, Jay Ess is feeling like Sherlock Holmes.

Note that this code runs in an environment where Jay Ess has access to three things: a require function, a module reference, and an exports reference. That’s basically all that’s needed for his code to work properly, and it doesn’t really matter what these things are and how they work at a low level. The loading can happen in a single bundled file, but it can also happen differently by loading on demand through an http request or on a file system (asynchronously, as we don't know how long something that runs outside our environment takes to finish).

1_Tfa5Fuid6Vz6oByiBvlXsQBase image from Pixabay

Chapter IX: Webpack and JavaScript

Now that Jay Ess is familiar with Browserify, his curiosity has grown. He continues his investigation by researching one of the most popular tools in this realm: webpack.

Webpack is a bundler for modern JavaScript applications. When webpack processes your application, it recursively creates a dependency graph that includes all the modules your application needs and then packages them into a small number of packages -- often just one -- to be loaded by the browser.

Learning of a new version of JavaScript, Jay Ess reads an article about features built into the language that can give him greater power over his code, and at the same time he starts to appreciate this lovely language, whose name, when abbreviated -- accidentally -- and definitely not on purpose -- is pronounced like his own.

Jay Ess discovers that, for a dynamic language, JavaScript has a very static module system, which has its limitations:

  • All imports/exports are only allowed at the top level in a module. There are no conditional imports or exports, and he can’t use import in the scope of any function. That's sad.

  • Everything must be explicitly exported by name. It’s impossible to loop and export multiple names according to some list. Where is the practicality in this? Maybe he needs a house-elf to do this for him, and his name could be Adobe instead of Dobby…

  • Module/export objects are petrified, and this scares Jay Ess because deep inside he remembers what Dumbledore once said regarding petrification: "Dark Magic of the most advanced kind." There’s no way out by adding features in a module object, like a polyfill would (worth searching if you don't know what this is).

  • All module dependencies must be loaded, parsed, and linked before any code from any module is executed. There is no syntax for an import that can be lazy loaded or loaded on demand.

  • There’s no way to handle import errors (e.g. no importing within a try/catch block). His application can have multiple modules in it, and if something can’t be loaded or bound in the path, basically nothing will be executed and Jay Ess will drink a fountain of the purest caffeine beverage on the market all night as he tries, without success, to troubleshoot using solely the language.

  • There’s no hook that allows a module to execute code before its dependencies are loaded. That is, modules have no control over how their dependencies are loaded

The JavaScript module system is really cool as long as Jay Ess’s needs are just static, but we know of all his sufferings, and he deserves more than this.

The good news? Tools like webpack allow for dynamism, such as code execution before dependencies are loaded and error detection at compile time. The module loading system Jay Ess uses will have a programmable API to walk along with this ES6 static import/export syntax. For example, webpack includes an API that he can use to "break the code" and load some packages of modules on demand (lazy). And this same API can help him kill most of the other limitations above!!

In fact, as Jason Orendorff says, “the static module syntax of JavaScript is designed to work in conjunction with some dynamic and programmatic API of a Module Loader,” so the static nature of ES6 can actually be a good thing when combined with webpack!

Beyond this, Jay Ess can use other cool webpack features like file mining, asset management, and code transformations like minification. Webpack can run transcompilers to transform TypeScript into JavaScript, for example. There are two syntaxes for writing code for importing and exporting modules: the CommonJS require("something") that Node.js uses and the ESM standard that is standardized in ECMAScript (import from "something").

Webpack supports both, transforming imports into require. You can’t imagine how ecstatic and relaxed Jay Ess is with so much power in hand.

To be even more explicit, webpack is both a bundler and a module loader. Some even call it a compiler. Webpack, not the browser, is responsible for all project assets, treating every file (.css, .html, .scss, .jpg, .etc.) as a module, even if it understands only JavaScript. So webpack’s loaders transform the files, treating them as modules and adding them to the dependency graph!

Jay Ess just wanted to manage his files efficiently, and he got so much more. These additional features came from the suffering of other characters who, like Jay Ess, needed new and better solutions.

Afterword

What I’ve learned as a developer is that, by accepting diverse and sometimes scary environments, I can view challenges with optimism. I remember that other developers may be experiencing the same difficulties, and these difficulties create opportunities for new tools. Webpack could have been developed by Jay Ess himself, as well as by each of you, dear readers. So why not start today and pioneer a solution for whatever challenge you face?


Author

Igor Rezende

Igor Octaviano is a UI Engineer at Avenue Code. He is a web developer, consultant, writer, musician, sometimes MMORPG enthusiast who values learning and teamwork when designing and developing applications.