13 Modules and bundlers
This chapter covers
- The twisty history of JavaScript modules
- The many virtues of the ESM standard
- How to make ESM play nice with CommonJS
- Transforming modules into optimized code with bundlers
In the beginning, there were <script> tags. In those early days, JavaScript code was bespoke: If a web page had any JavaScript at all, it was almost certainly written for that specific web page.
Soon, though, JavaScript libraries started to emerge. And as soon as they did, code started to collide: The only way to “export” code from a library was to declare a global variable with a (hopefully unique) name. Developers yearned for a solution. A slew of competing module standards quickly emerged. Node.js adopted a module system called CommonJS, which was simple on the server but couldn’t translate to the browser since it expected module loading to be a synchronous operation. On the browser side, chaos reigned.
Because there was no native syntax for expressing inter-module dependencies in the browser, conventions like AMD and UMD emerged that required modules to be expressed as functions, with a list of other modules to load as dependencies. That patched the problem, but it wasn’t pretty to look at. Worse, it meant the module graph could only be seen by actually running the code. There was no practical way to construct the module graph at build time to prune unused code.