Last week, we set the stage for understanding the origin of bundlers: our protagonist Jay Ess successfully solved the problem of his single, monstrous script file by breaking it into several smaller files, but now he faces new challenges and must find better solutions...

Part 2: Exploring Solutions

Chapter IV: The First Script

In our last installment, Jay Ess was struggling to maintain his massive script file, a task made all the more complicated because he had a development team working on it alongside him. He solved this issue by breaking his script into several small files, but now he has the following problems:

1. He has a lot of <script> tags on his page

2. He has to keep a certain order

3. Coworkers complain that it takes too long to load 200+ files

Wanting to make some progress, Jay Ess begins thinking through how to solve his first problem--having a lot of <script> tags. Now, there is actually a simple solution to this problem. It won't solve all his problems, but it will help our poor developer stay on track: he could have a shell script or another similar tool that would simply concatenate all his script files in a certain order. This would solve his first problem (since he has 274 or more files by now). Using this method, the script that was included on his page would be condensed into a single script file. It could return a single <script> tag on his page.

"What great progress!" exclaimed Jay Ess after a loud giggle as he read about this possible solution I mentioned in a post on the internet (of course). But still he has the second problem at hand: for the shell script to properly concatenate all JavaScript files, it has to maintain the correct order. Jay Ess can think of several hairy solutions, and maybe some of them can partially solve his problem.

In a fictional situation (inside a fictional story), during a period of insight into the radiant light of eternal wisdom, he then thought, "Maybe I can name my files following a pattern, like 00001-someArchive.js, 00002-anotherFile.js ..., and then proceed with concatenating following that order. Well ... keeping this going would be a bit chaotic, but it would work!"

So that's what he did. At first, he used a numeric sequence, but after having to rename 80 files one terrible day, he left some empty spaces to fill when needed. (Jay Ess… such a smart guy.)

Well, as we can see, any solution that follows this line of thinking is complicated or becomes complicated very quickly, and it also does not solve Jay Ess's problem in a practical way. Is it a valid attempt at solving it? Yes, but it is only an analgesic, relieving the pain momentarily.  Deep down, Jay Ess knows there are more sophisticated methods available, so he resorts again to the web oracle to seek wisdom. 

Chapter V: A First Approach to Modules

After reflecting on this for a while, Jay Ess arrived at the idea that it would be really cool if he could add some form of his own script file and make his own order, or rather, make his dependencies explicitly available.

Screen Shot 2018-08-15 at 18.16.53Base image from Pixabay

That’s when he discovered RequireJS, a module loader similar to his idea! In summary, RequireJS is what many people say it is--a Module Loader. RequireJS implements a definition called the Asynchronous Module Definition, which loads modules asynchronously:

// myShirt.js now have dependencies, the shopping cart and the inventory

// module in the same directory as myShirt.js

define (["./ cart", "./inventory"], (cart, inventory) => {

        // returns an object to define the module "myShirt".

        return {

            color: "blue",

            size: "large",

            addToCart: () => {

                inventory.remove(this);

                cart.add(this);

            }

        }

    }

);

Now, while he was making use of RequireJS, living happily immersed in the comfort of this world, some guys posted something called Node.js on the web, and this thing became extremely popular! This is relevant because Node.js includes a mechanism that does exactly what Jay Ess wants to do: it defines modules that can have local and private parts, export some public parts, and require other modules/dependencies.

Node.js had all the power of a popular syntax of a specification known then as CommonJS. The syntax in Node.js works great on Node.js, but module support wasn't fully available for all browsers at the time. After a while, the ECMAScript standard decided to use another similar mechanism and a new extensible syntax. The takeaway? It's smart to follow popular syntaxes since they have a good chance of being maintained by the community. 

When Jay Ess finally discovered that this syntax would free him from the evil of that weird code about closures and allow him to write modules in a cleaner and more uniform manner, he decided to follow the Node.js path:

// request dependency:

let a = require ('a.js'); // Node.js modules

import a from 'a.js'; // ES modules

// ... your code here ...

// a way to turn the things visible outside the module:

module.exports = something; // Node.js

export something;

Screen Shot 2018-08-15 at 18.21.05

Photo by Sam Mgrdichian on Unsplash

Chapter VI: The Second Script

Since Jay Ess still needed to package all his files into a single file and make that file work in his browser (not Node.js), he had to revise his shell script, which only concatenated all his JavaScript files, into something more sophisticated.

Here’s how he did this: Before each script file, the shell script would insert a number of generic utility functions because the browser does not support the Node.js (require, module.exports) syntax.

The module code will be written easily, and when the time comes to execute, it will have to manage how and when it will run. If the code calls require ("something.js"), its utility generic functions will make the code available for that module. Usually this is done through records or identifiers that allow the system to reference the modules correctly, as if they were referencing common files.

Chapter VII: His Own Bundler

Let's recap again!

Jay Ess now has a manageable code base with source code that is functionally divided into small modules. It also has a process or a tool that:

1. Puts all his small files into a single file

2. Adds generic utility functions that:

    1. Make each module available along with other required modules
    2. Solve his sorting problem

Hey wait a minute! How on earth has the sorting problem been solved? I lost that part! Well, now module execution can be delayed if any dependencies aren’t yet loaded (asynchronous loading)! So Jay Ess no longer has to worry about order. It simply loads all modules and then executes what it wants to execute.

Note that this is not the only benefit. The dependency system can work with its code, either encapsulated or grouped into a single file, or even, if appropriate, a separate file that was loaded on demand/import. As long as the system or tool makes the mechanisms available and understands the same syntax, Jay Ess acquires that capability under his code without his code knowing about it (remember SOLID? No? Go read the principles, paying special attention to the dependency inversion part!).

Now this tool that Jay Ess utilized is one that we can use ourselves, just as we imagine here in this story. But wouldn’t it be easier if several people made similar tools that would work together, or else used the same tool we're using? Then we could treat external libraries how we treat our own code--like a contract or a common interface. Instead of building a tool ourselves, it's smart to use an existing one. These tools are Browserify, webpack, Parcel, etc. 

Some of these tools take advantage of the fact that we are already doing all these code transformations, as well as packaging processes, to provide other tasks like minifying our code (compressing it and reducing load time). Other tools are sophisticated enough to prevent the inclusion of files (or file parts) that aren’t in use (a technique known as tree-shaking). They can also process assets like CSS and images. Why not make use of everything?

COMING SOON!

Want to know what's next? In our third and final installment, we'll examine the generated code and introduce one of the most popular tools available--webpack!


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.