# Let's Talk Code Toolings: Bundlers

A **bundler** is a tool that takes multiple **JavaScript, CSS, and other assets** and combines them into a **single (or a few) optimised files** for efficient loading in a web browser.

## **What Does This Mean?**

Let’s break this down with an example.

Imagine you're building a **calculator app** in JavaScript. Instead of writing all your code in one massive file, you decide to split it into multiple files to keep things organised.

### **Project Structure**

```plaintext
calculator-app/
│── index.html
│── style.css
│── index.js
│── math.js
│── utils.js
```

Each file handles different parts of the app:

* `index.html` → Serves as the entry point for the browser
    
* `style.css` → Manages the visual styling and layout of the app
    
* `math.js` → Handles calculations
    
* `utils.js` → Provides helper functions
    
* `index.js` → Controls the app’s main logic
    

### Logic Breakdown

#### **1.** `math.js` (Handles Math Operations)

```javascript
export const add = (a, b) => {
  return a + b;
}

export const subtract = (a, b) => {
  return a - b;
}
```

#### **2.** `utils.js` (Helper Functions)

```javascript
export const printResult = (result) => {
  console.log(`Result: ${result}`);
}
```

#### **3.** `index.js` (Main Application Logic)

```javascript
import { add, subtract } from "./math.js";
import { printResult } from "./utils.js";

const result = add(5, 3);
printResult(result); // Output: Result: 8
```

Now, this setup **seems fine**, but as your project grows, you'll start facing challenges\*\*.\*\*

## Problems Without a Bundler

1. ### **Compatibility Issues**
    
    Older browsers (like Internet Explorer) don’t support **ES module imports (**`import { add } from "./math.js";`). You would need to manually include [polyfills](https://developer.mozilla.org/en-US/docs/Glossary/Polyfill) or rewrite your code to work with older JavaScript module systems (like CommonJS).
    
2. ### **Redundant Code (Unused Functions and Comments)**
    
    Right now, our `subtract()` function is never used. But when the browser loads `math.js`, it still loads that function, wasting resources.
    
    Other unnecessary parts of the code include:
    
    * **Comments** that don’t need to be in the final file
        
    * **Extra white spaces** that make the file larger
        
    
3. ### Too Many HTTP Requests
    
    Each **import statement** in `index.js` makes a separate request to the server.
    
    Imagine you have **50+ JavaScript files**, your browser has to load each one individually, making **50+ network requests**. This slows down page load speed.
    
4. ### **Dependency Management Issues**
    
    If you use third-party libraries (like [**Lodash**](https://lodash.com/)), you typically install them via **npm**.
    
    Let’s modify our **calculator app** to use Lodash:
    
    #### **Updated** `index.js`
    
    ```javascript
    import { add, substract} from "./math.js";
    import { printResult } from "./utils.js";
    import _ from "lodash"; // Import Lodash (third-party library)
    
    const result = add(5, 3);
    printResult(result); // Output: Result: 8
    
    const numbers = [2, 4, 6, 8];
    const sum = _.sum(numbers); // Uses Lodash to sum numbers
    printResult(sum); // Output: Result: 20
    ```
    
    However, **this will fail in the browser** :
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1738191657931/25da393e-054c-41e4-b6b0-7e4ad778140a.png align="center")
    
    This occurs because:
    
    1. **Browsers don’t understand Node.js module resolution**:
        
        * When you use `import _ from "lodash"`, the browser doesn’t know how to resolve the `lodash` package from `node_modules`.
            
        * Browsers expect module paths to be relative (e.g., `./math.js`) or absolute (e.g., `/path/to/file.js`).
            
    2. **Lodash is installed in** `node_modules`:
        
        * The `lodash` package is installed via npm and resides in the `node_modules` folder, which browsers cannot access directly.
            
    

## How Does a Bundler Solves These Challenges?

A bundler processes your files and resolves the issues we identified earlier—making your app faster, more efficient, and browser-compatible. Let’s break down the process step by step, referencing our **calculator app example**.

### Step 1: Entry Point Discovery

The first step in bundling is **entry point discovery**. The bundler starts from the **main file** (in this case, `index.js`) and systematically follows all **import statements** to determine which files and dependencies are required to run the application.

#### **Updated** `index.js`

```javascript
import { add, substract} from "./math.js";
import { printResult } from "./utils.js";
import _ from "lodash"; // Import Lodash (third-party library)

const result = add(5, 3);
printResult(result); // Output: Result: 8

const numbers = [2, 4, 6, 8];
const sum = _.sum(numbers); // Uses Lodash to sum numbers
printResult(sum); // Output: Result: 20
```

#### **Dependencies Identified by the Bundler:**

1. `math.js` → contains `add()` and `subtract()` functions
    
2. `utils.js` → contains `printResult()`
    
3. `lodash` → an external package providing the `_.sum()` function
    

### **Step 2: Creating a Dependency Graph**

After discovering all dependencies in **Step 1**, the bundler constructs a [**dependency graph**](https://en.wikipedia.org/wiki/Dependency_graph)—a structured representation of how different files are interconnected. This ensures that no required module is left out and helps optimize how the code is bundled.

Given our `index.js` file:

```javascript
import { add, subtract } from "./math.js";
import { printResult } from "./utils.js";
import _ from "lodash"; // Import Lodash (third-party library)
```

The bundler generates the following **dependency graph**:

```plaintext
index.js  
 ├── math.js  
 │   ├── (add function)  
 │   ├── (subtract function)  
 ├── utils.js  
 │   ├── (printResult function)  
 └── lodash (npm package)  
     ├── (sum function)
```

This visualises how `index.js` relies on `math.js`, `utils.js`, and `lodash`, and how each of these files contribute specific functions.

### Step 3: Code Transformation

In this step, the bundler takes the modern JavaScript code (or other assets like CSS) and transforms it into a format that’s compatible with all browsers, even older ones. This is especially important because not all browsers support modern JavaScript features like ES modules (`import`/`export`).

#### What Happens Here?

1. [**Transpiling Modern JavaScript**](https://theody.hashnode.dev/lets-talk-transpilers):
    
    * The bundler uses tools like [**Babel**](https://babeljs.io/) or [**SWC**](https://swc.rs/) to convert modern JavaScript (ES6+) into older versions (ES5) that older browsers can understand.
        
    * Arrow functions (`() => {}`) are converted into regular functions (`function() {}`).
        
    * `const` and `let` are converted to `var`.
        
    * Template literals (`` `Result: ${result}` ``) are converted to String concatenation (`"Result: " + result`)
        
2. **Transforming** `import` to `require`:
    
    * Modern `import` statements are converted into `require` statements, which are compatible with older environments.
        
    * Similarly, `export` statements are converted into `module.exports`.
        
3. **Handling Non-JavaScript Files**:
    
    * If your project includes CSS, images, or other assets, the bundler will process these too. For example, it might convert SCSS into plain CSS or optimise image sizes.
        
4. **Resolving Module Paths**:
    
    * The bundler ensures that all `import` statements are correctly resolved. For example, if you import `lodash` from `node_modules`, the bundler will locate the correct file and include it in the bundle.
        

**Let’s use the** `calculator-app` **example to show how the bundler might transform the code.**

#### Before Transformation (Modern JavaScript with ES Modules):

```javascript
// math.js
export function add(a, b) {
  return a + b;
}

export function subtract(a, b) {
  return a - b;
}

// utils.js
export function printResult(result) {
  console.log(`Result: ${result}`);
}

// index.js
import { add } from "./math.js";
import { printResult } from "./utils.js";
import _ from "lodash";

const result = add(5, 3);
printResult(result);
```

#### After Transformation (CommonJS and ES5):

```javascript
// Transformed math.js
function add(a, b) {
  return a + b;
}

function subtract(a, b) {
  return a - b;
}

module.exports = { add, subtract };

// Transformed utils.js
function printResult(result) {
  console.log("Result: " + result); // Template literal transformed
}

module.exports = { printResult };

// Transformed index.js
var math = require("./math.js");
var utils = require("./utils.js");
var _ = require("lodash");

var result = math.add(5, 3); // const transformed to var
utils.printResult(result);
```

### Step 4: Code Optimisation

Once the code is transformed, the bundler optimises it to make it smaller, faster, and more efficient. This step ensures that your app loads quickly and doesn’t waste resources.

#### What Happens Here?

1. [**Tree Shaking**](https://developer.mozilla.org/en-US/docs/Glossary/Tree_shaking):
    
    The bundler removes unused code. For example, since the the `subtract` function in `math.js` is never used, it won’t be included in the final bundle. This reduces the file size.
    
2. [**Minification**](https://developer.mozilla.org/en-US/docs/Glossary/Minification):
    
    The bundler removes unnecessary characters **like comments, whitespace, and long variable names.** **For example:**
    
    ```javascript
    // Before minification
    function add(a, b) {
      return a + b;
    }
    ```
    
    **After minification:**
    
    ```javascript
    function add(a,b){return a+b}
    ```
    
3. **Code Splitting**:
    
    Instead of generating **one large file**,, the bundler can split the code into smaller chunks (files) that load only when needed. This improves performance by reducing the initial load time.
    
4. **Optimising Dependencies**:
    
    In scenarios where third-party libraries like Lodash are used, the bundler ensures only the parts needed are included. For example, since only the `sum` function from Lodash was used, the bundler will only include that and not the entire library.
    

### Step 5: Generating the Final Output

After transforming and optimising the code, the bundler generates the final output—a single (or a few) bundled files that your browser can load efficiently.

#### What Happens Here?

1. **Creating the Bundle**:
    
    The bundler combines all the transformed and optimised code into one or more files. For example, it might create a single `bundle.js` file that includes your app’s logic, dependencies, and assets.
    
2. **Generating Source Maps**:
    
    [Source maps](https://web.dev/articles/source-maps) are files that help you debug your code in the browser. They map the bundled code back to the original source files, so you can see where errors occurred in your original code.
    
3. **Outputting Files**:
    
    The bundler saves the final files to a specified directory (e.g., `dist/` or `build/`). These files are ready to be deployed to a web server.
    

#### Example:

After bundling, your project might look like this:

```plaintext
calculator-app/
│── dist/
│   ├── bundle.js
│   ├── style.css
│   ├── index.html
```

* `bundle.js` contains all your JavaScript code, including dependencies like Lodash.
    
* `style.css` contains all your CSS, optimised and minified.
    
* `index.html` is updated to include the bundled files.
    

## **Popular Bundlers and Their Use Cases**

| **Bundler** | **Usage** |
| --- | --- |
| [**Webpack**](https://webpack.js.org/) | The most powerful and configurable bundler, widely used in large projects. |
| [**Vite**](https://vite.dev/guide/why) | Lightning-fast bundler optimised for modern frameworks like React and Vue. |
| [**Parcel**](https://parceljs.org/) | Zero-config bundler, great for beginners and fast builds. |
| [**Rollup**](https://rollupjs.org/) | Best for libraries and optimising ES modules. |
| [**esbuild**](https://esbuild.github.io/) | Extremely fast bundler written in Go, used for instant builds. |

## In Conclusion,

A **bundler** is an essential tool for modern web development, transforming how we build and optimise applications. By combining multiple JavaScript, CSS, and asset files into a single (or a few) optimised bundles, it addresses critical challenges like browser compatibility, redundant code, excessive HTTP requests, and dependency management.
