Frontend Development 12 min read

Deep Dive into Webpack: Source Code Analysis and Compilation Process

The article dissects Webpack’s internals, showing how every asset becomes a module processed by loaders, how the bootstrap creates a __webpack_require__ function to link modules, how dynamic import() generates separate chunks loaded via script tags, and outlines the full compilation pipeline from configuration parsing to final asset emission.

Didi Tech
Didi Tech
Didi Tech
Deep Dive into Webpack: Source Code Analysis and Compilation Process

Webpack treats every static asset as a module, processes them through a series of loaders, and finally emits bundled js , css , images, fonts, etc. This article walks through the internal source code of webpack to explain how resources are parsed, how different loaders handle them, and how the modules are linked together into the final bundle.

1. Simple example before bundling

Entry file main.js imports a.js and b.js :

// main.js
import { A } from './a';
import B from './b';
console.log(A);
B();

// a.js
export const A = 'a';

// b.js
export default function () {
    console.log('b');
}

After webpack processes these files, the output bundle.js contains an immediately‑invoked function that receives a modules object. Each original file becomes a function stored under its relative path key:

(function(modules) {
    // webpack bootstrap code …
    return __webpack_require__(__webpack_require__.s = "./demo01/main.js");
})({
    "./demo01/a.js": function(module, __webpack_exports__, __webpack_require__) { … },
    "./demo01/b.js": function(module, __webpack_exports__, __webpack_require__) { … },
    "./demo01/main.js": function(module, __webpack_exports__, __webpack_require__) { … }
});

The bootstrap defines __webpack_require__ , which loads a module, caches it, executes the module function with module , module.exports , and __webpack_require__ as arguments, and finally returns module.exports :

function __webpack_require__(moduleId) {
    if (installedModules[moduleId]) {
        return installedModules[moduleId].exports;
    }
    var module = installedModules[moduleId] = {
        i: moduleId,
        l: false,
        exports: {}
    };
    modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
    module.l = true;
    return module.exports;
}

When an import statement is compiled, webpack replaces it with a call to __webpack_require__ . For named exports it creates a property on __webpack_exports__ :

__webpack_require__.d(__webpack_exports__, "A", function() { return A; });
// equivalent to
__webpack_exports__.A = A;

For a default export, webpack adds a default property to module.exports :

__webpack_exports__["default"] = function () { console.log('b'); };

2. Asynchronous loading

Webpack supports dynamic import() which returns a Promise . The requested module is placed in a separate chunk (e.g., 0.js ) and loaded by inserting a <script> tag. The global array window["webpackJsonp"] stores pending chunks. Its push method is overridden by webpackJsonpCallback to register the new modules and resolve the stored promises:

function webpackJsonpCallback(data) {
    var chunkIds = data[0];
    var moreModules = data[1];
    // resolve promises for loaded chunks
    for (var i = 0; i < chunkIds.length; i++) {
        var chunkId = chunkIds[i];
        if (installedChunks[chunkId]) {
            resolves.push(installedChunks[chunkId][0]);
        }
        installedChunks[chunkId] = 0;
    }
    // add new modules to the module map
    for (var moduleId in moreModules) {
        if (Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
            modules[moduleId] = moreModules[moduleId];
        }
    }
    if (parentJsonpFunction) parentJsonpFunction(data);
    while (resolves.length) {
        resolves.shift()();
    }
}

When multiple webpack bundles are loaded on the same page, they share the same webpackJsonp array. The callback logic ensures that a chunk loaded by one bundle is not fetched again by another, preventing duplicate network requests.

3. Overall compilation flow

The high‑level steps are:

Parse the webpack configuration (entry, output, loaders, plugins).

Resolve the entry file path to an absolute path.

Recursively load each file, applying the appropriate loaders (e.g., Babel for JS, css‑loader for CSS).

Collect dependencies, build a dependency graph, and create module objects.

Group modules into chunks based on entry points and split‑point configuration.

Emit the final assets (bundle files, source maps, etc.).

The article promises a series of deeper dives into specific parts of the webpack source such as Tapable hooks, module resolution, loader handling, module generation, chunk generation, and final asset emission.

Author: Cui Jing, Senior Front‑End Engineer at Didi. The article also includes a recruitment notice for senior front‑end engineers.

JavaScriptfrontend developmentAsync LoadingModule Bundlingwebpack
Didi Tech
Written by

Didi Tech

Official Didi technology account

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.