Build Tooling & Bundling

Modules to dependency graph to transforms to chunks. Code splitting, tree shaking, TypeScript, polyfills versus transpilation, source maps, and what your users download.

The Module Graph
▶ lab

Entry points become a dependency graph, the graph is transformed, and the graph is cut into chunks. Every bundler is an implementation of those three ideas.

Q · What does a build tool actually do between my `import` statements and the files a browser downloads?
Bundlers Compared
▶ lab

Vite, Webpack, Rollup, esbuild and SWC as implementations of the same graph-and-chunks model, separated by dev-server strategy, build speed, plugin surface and output control.

Q · These tools all produce JavaScript files — what actually differs between them, and which difference should decide anything?
Code Splitting
▶ lab

Cutting one graph into an initial chunk plus route and feature chunks — and the two failures on either side of it: shared code duplicated, and a waterfall of tiny chunks.

Q · My application ships as one large file that every user downloads in full. How do I cut it up without making things worse?
Lazy Loading
▶ lab

Deferring routes, components, images and expensive modules until they are needed — and the loading state, the flash and the error path that every deferral creates.

Q · Which parts of this page can I load later, and what do I owe the user in the gap I just created?
Tree Shaking
▶ lab

Static module analysis can allow unused exports to be dropped — and five specific things routinely stop it: side effects, a wrong `sideEffects` flag, CommonJS interop, barrel re-exports and dynamic access.

Q · I import one function from a large library. Why did the whole library end up in my bundle?
Bundle Analysis
▶ lab

Reading a treemap: attributing bytes to modules, finding the dependency nobody meant to add, and separating "large" from "large and on the critical path".

Q · The bundle grew. What is in it, who put it there, and does any of it matter to the user?
Minification Is Not Compression
▶ lab

Minification rewrites the artifact and is permanent. Compression encodes the response and is undone by the browser. They compose, they are configured in different places, and they are not alternatives.

Q · My build minifies and my server compresses. Are those the same optimisation done twice, or two different things?
ESM vs CommonJS
▶ lab

Static structure known before execution versus a runtime function call that returns an object — and why the first is what makes tree shaking and named-export analysis possible at all.

Q · Why does the module format of a dependency change what my bundler can do with it?
Polyfills vs Transpilation

Transpilation rewrites syntax an engine cannot parse. A polyfill supplies an API an engine does not have. Neither can do the other's job.

Q · The code works in my browser and throws in theirs. Do I need to transpile it, polyfill it, or neither?
Source Maps

The mapping from the bundle that shipped back to the source you wrote. Without one, a production stack trace names a minified letter on line 1.

Q · An error came in from production pointing at `t` in `app.4f2c.js:1:88214`. How do I find out what that is?
TypeScript in the Build

Parse, type check, emit. The types are erased before anything runs, so nothing they promised is enforced at the network boundary.

Q · The response is typed as `Order`. Why is `order.total` undefined at runtime?