Webpack Deep Dive Part 4: Module Creation

Randall Naar
5 min readJan 1, 2021

Introduction

It’s been a while since my last post but I’m finally back! I attribute my slow pace of publishing to my previous lack of structure, but now I’ve decided to dedicate time towards creating a structural framework for my future posts. Below is a detailed description of the sections that will compose my future posts:

Introduction - This is where I’ll discuss updates to my blogging schedule, recent roadblocks I’ve overcome, and my overall blogging experience in general.

Recap - This will give context on where we are in our exploration of the webpack codebase. It will cover what we did in the last post and should set the stage for the current post.

Scope - This will tell the reader which files and methods I’ll be reviewing along with which commit of webpack I’ll be looking at.

Method Summaries - This section reveals what I learned about each method described in the scope. The methods will usually be listed in the order that they appear in the call stack so that the summaries can lead into each other.

Annotated Code Inspection - This is where I’ll comment on the code itself, leaving inline comments describing the implementations of various methods.

Recap

Up until this post we’ve been following what the webpack code does when run with it’s default configuration. We do this by calling the webpack function offered as part of the node API and passing in an empty options object. Using the ‘webpack’ function as an entry point to the webpack codebase, we explored the inner logic of the function towards the goal of understanding the entire bundling flow.

On a surface level the ‘webpack’ function delegates its logic to other functions within the ‘webpack.js’ file. The 2 functions it delegates to are ‘createCompiler’ and ‘run’. ‘createCompiler’ configures the default values for the options object passed in and creates a ‘compiler’ object to return. The ‘run’ method is then called on the ‘compiler’ and the webpack function terminates.

Looking deeper into the compiler class, the code for the ‘run’ method calls another inline function labeled ‘run’. I won’t get too much into code critique here but worry not, I’ll circle back to this. The inline ‘run’ nested in the original ‘compiler.run’ method calls some hooks and delegates the execution to yet another method titled ‘compile’. In the ‘compile’ method a ‘compilation’ object is made and more hooks are invoked (with only some taking the ‘compilation’ object).

Looking at the structure of the code, it’s safe to say most of the logic is actually encapsulated within these hooks. The hooks ‘beforeRun’, ‘run’, ‘beforeCompile’, ‘compile’, ‘make’, and ‘finishMake’ (defined on the ‘this.hooks’ property on the compiler object) seem to collectively enable the bundling behavior, but what each hook does individually is a mystery I hope to unravel starting with this blog post.

The two hooks that accept the compilation object are ‘make’ and ‘finishMake’ which suggests that the ‘make’-related hooks are good candidates for further analysis. Looking at the attached functions for the ‘make’ hook (the functions that are invoked when the hook is called), the function registered with the identifier ‘EntryPlugin’ specifically seems to push the bundling forward. The hoooked ‘EntryPlugin’ function calls the ‘addEntry’ method on the compilation object which is what we’ll be looking at for this post.

Scope

The scope of this post will cover the methods ‘compilation.addEntry’ (invoked from the ‘make’ hook), ‘compilation._addEntryItem’, ‘compilation.addModuleChain’, and ‘compilation.handleModuleCreation’ within the file ‘compilation.js’. The code I’m analyzing is from commit da67529a2 of webpack and I’ll only be covering the execution paths taken when using an empty webpack options object.

addEntry

The addEntry method enforces the ‘options’ parameter is an object and calls ‘_addEntryItem’ to take care of the real logic.

_addEntryItem

This method makes an ‘entryData’ object and sticks the ‘entry’ parameter in the ‘dependencies’ array property. This method then puts the ‘entry’ parameter in the ‘this.entries’ map (using the string “main” as a key) and calls the ‘addModuleChain’ method.

addModuleChain

‘addModuleChain’ gets something called the NormalModuleFactory from ‘this.dependencyFactories’ using the class constructor of the entry parameter as a key (‘entry.constructor’). This method then passes the created module factory into ‘handleModuleCreation’ along with the ‘dependency’ it received as a parameter.

handleModuleCreation

This is the meatiest method of this section. It calls the compiler class’s ‘factorize’ method and passes the returned, ‘factorized’ module into another method called ‘addModule’. The resultant module returned from ‘addModule’ is added to something called a dependency graph, checked for cyclic build dependencies before being passed into ‘buildModule’. The module that’s built is passed into ‘processModuleDependencies’ which scans the built module for dependencies and passes each of them into ‘handleModuleCreation’ leading to a recursive cycle of building modules.

Annotated Code Inspection

context— The directory webpack was called from

entry — An object containing information on the path for the file being processed

optionsOrName — an object where, in our example, everything is set to null except the name

callback — a callback that will resume execution of the bundling process

I’ll only write these parameters out once since all the subsequent methods take some variant of those same parameters. JSDoc comments exist as well, but hopefully I can give more information on what the parameters actually are with my informal descriptions above.

Now, let’s take a look at the ‘addEntry’ and the ‘_addEntryItem’ methods below:

‘addModuleChain’ doesn’t do much in terms of logic. It basically just creates a module factory and passes that on to the next method ‘handleModuleCreation’:

Next we’ll go over the code of the last method in this post, ‘handleModuleCreation’:

Next Time

Hopefully this post was a good introduction to these methods and you have a (very) rough idea of where the execution is headed. In the next post, Part 5, I’ll get into more detail about how ‘factorizeModule’ works and lend my comments to that code snippet as well. Until then, stay on the lookout for my next post and feel free to leave suggestions on how I can improve this series. As always, thanks for reading!

--

--