loading...
Cover image for It Isn't Magic, It's Webpack.

It Isn't Magic, It's Webpack.

crishanks profile image crishanks ・5 min read

Bundlers

Maybe a better title for this post would have been "It Isn't Magic, It's Webpack (or Browserify, Brunch or Parcel, etc.)." These are known as module bundlers, which have been defined as packages of code that "compile small pieces of code into something larger and more complex that can run in a web browser." In essence, this will package all of your stylesheets into one package, your TypeScript into another, all of your JavaScript files into yet another, etc.

Module bundlers essentially figure out which bits of code depend on other bits of code (in other words, bundlers identify dependencies) and make sure code runs in the order it needs to. Bundlers ultimately create a dependency graph, starting with a root (which has no dependencies) wherein the further down the graph a bundle of code falls, the more dependencies it has. Further-down code waits its turn for its dependencies to load first before it is loaded itself.

So, What Is Webpack?

Even in simple applications, we write a lot of code in a lot of separate files. We use syntax like @import and ES6. We use helpers like TypeScript to which allow us to write clearer code and catch errors sooner. Think of applications built with component-based libraries and frameworks like React where we import components or functions that depend on code written somewhere else in our application. The way our code is parsed, packaged, and executed in a way our browsers can understand can seem like nothing short of magic. But it isn't. It's a bundler called Webpack.

What makes Webpack stand out, though, is its ability to gather all dependencies including not just code, but assets such as images, stylesheets (including preprocessors like sass, typescript and more and create the beforementioned dependency graph. And that's the key -- the heart of Webpack is building the dependency graph.

Building the Dependency Graph

The dependency graph consists of a few key components. Here I'll focus on: Entry, Output, Loaders, and Plugins. One can run yarn add webpack webpack-dev-server --save-dev and create a webpack.config.js file to your app's root directory to get started. Don't forget to update your package.json scripts:

"start": "webpack-dev-server",
"build": "webpack"

1. Establish an Entry Point

The first thing Webpack does is establish an entry point. This is going to be the root of the dependency graph. We can do this by creating a variable containing an object which points to the source file. This is typically going to be index.js. You can create the object with a simple string, but for scalability and the possibility of a need for multiple entry points, let's use an object.

entry point example

Simple enough.

2. Establish an Output

Once Webpack has finished bundling code and creating the dependency tree, it needs to be told where to put the finished product; This is our Output. Remember, you can name the filename parameter whatever you'd like. Typically this will be named something like main.js.

output example

Interestingly enough, this is the base information the application needs to bundle code. With this, you can spin up your local server with yarn start and Webpack will begin doing its thing.

3. Loaders: Bundling File Types Beyond JavaScript

This is where Webpack starts getting so cool.

Here's a screen grab of some imports from the index.js file of a recent React project of mine called Squad.

imports example

It's something we do 100 times a day. But what's really happening behind the scenes? We're telling Webpack about the dependencies index.js needs. For example, the line import ./index.css tells Webpack to grab those styles.

In our webpack.config.js file, we add a module object like the example below (see webpack concepts, loaders).

loaders module

This object uses a Regular Expression to identify certain file types and then tells Webpack which loader to use before bundling. We're saying, "Webpack compiler when you find a path that ends with .css in an import, transform/load them using the css-loader and style-loader when you bundle."

Important note: "Transforming" means parsing files (other than .js files), and translating it into something else that Webpack and the browser can understand.

A few examples:

  • Have you ever used @import or syntax like url('./icon.png') in your CSS files? Webpack's css-loader is the reason why! It parses your .css file and processes it. That is why you can import Icon from ./icon.png; and later element.appendChild(Icon)! As stated in the Webpack Documentation, "The loader will recognize this is a local file, and replace the ./icon.png path with the final path to the image in your output directory. The html-loader handles <img src="./icon.png" /> in the same manner." Isn't that so cool?
  • TypeScript is a superset of JavaScript that (among other things) allows JS devs to write more statically-typed code to catch errors while writing code. But Webpack doesn't speak TypeScript. The ts-loader parses TypeScript files and converts them into JavaScript Webpack can read.
  • What about any ES6 syntax like classes, let or const variables, arrow functions, default values, destructuring, etc? babel-loader parses the code you wrote, identifies ES6 syntax, and loads and transpiles it to ES5 the browser understands.

You can learn more about how loaders work under the hood, and even how to write your own loaders here.

4. Plugins vs. Loaders

Loaders

  • Work on a file level
  • Help create the bundle (during or before bundle generation)

Plugins

  • Work on a system level
  • Affect the created bundle (bundle or chunk level)
  • Focus on optimization (uglifyJS takes the bundle.js and minimizes it to decrease file size)

The code implementation looks something like this:

plugins example

The following graphic may help visualize the difference as well:

plugin vs. loader diagram

Summary

Webpack is the culprit behind much of the magical secret sauce that allows us to use syntax and libraries that makes code cleaner, clearer, more scalable. From imports to ES6, developing apps would be difficult without bundlers like Webpack.

Posted on by:

crishanks profile

crishanks

@crishanks

Full Stack Developer React | Ruby on Rails | NodeJS

Discussion

markdown guide
 

When I first saw the title of your article, I immediately thought of Arthur C. Clarke's third law:

Any sufficiently advanced technology is indistinguishable from magic.

 

I can certainly see myself using that quote in the future! I often find myself attempting to draw back the curtain and find out how the magic trick is done.