chapter thirteen

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.

13.1 Understanding ESM

13.1.1 Importing and exporting in ESM

13.1.2 Resolving imports with entry points and maps

13.2 Bridging ESM with CommonJS

13.2.1 The double-default problem

13.2.2 The top-level await problem

13.3 Building JS bundles

13.3.1 The art of code splitting

13.3.2 Targeting browsers with transpilation

13.4 Summary