In Part 1 of this series we learned about the packages that reside within both the webpack and webpack-contrib GitHub organizations! In Part 2, we discovered
Tapable , learned that it is a ~230 line library similar to NodeJs’s
EventEmitter , and found that it powers the entire [webpack] plugin system.
In addition to this, we learned how webpack creates tapable instances (Classes that
extend Tapable ) and how [webpack] plugins register to them and execute their functionality. Finally, we learned about each tapable instance that exists in webpack and what their purpose is.
Building the Dependency Graph
In this article, we are going to combine what we’ve learned so far, and stitch together a high level explanation of how webpack builds the dependency graph.
The dependency graph is one of the key features of webpack, and we believe understanding how it works, can bring much insight to contributors and users alike.
Throughout this information you will find diagrams that come from my talk Everything’s a plugin: Mastering webpack from the inside out. These diagrams will serve as an aide to the material but we also recommend watching the first half of the talk as a companion to this guide.
Step 1: Initialize (Compiler)
Given that we already have a valid webpack configuration (known as the compiler options), the first tapable instance that we are going to encounter when webpack runs is known as the
Compiler . Consider the
Compiler as “central dispatch” because it is only in charge of triggering high level events such as
"done" . The Compiler will always return a
Compilation, and other important tapable instances are attached such as
Compiler has instantiated all plugins and objects needed to compile, it will return a
new Compilation .
Step 2: Begin Compilation (Build the Graph)
Compilation (yes another tapable instance).
We describe the
Compilation as your application’s dependency graph. To create a full graph, we must start from somewhere; a “root node” that branches out to all other “nodes”.
What we’re describing is the entry property in your configuration. Although we provide a path to the entry point, webpack still needs to confirm that path exists. Below will begin a recursive set of operations. (We’ll jump back to the recursion later)
Any time that a raw request (path to a module) is provided — in this case its the entry point — webpack will first send this path information to the
Resolver instance. The
Resolver will use an enhanced NodeJs resolution pattern to to ensure the module at the given path exists, and then return additional information about that resolved module. This information includes file system stats, absolute path, and a unique ID for that resolved module.
- Parse Module: The module factories will also assign the
Parserinstance to each
NormalModuleobject that the factory will create. After the module source has been stored, the
Parser(powered by acorn) can now begin to parse the source and generate an Abstract Syntax Tree (AST).
- Find Dependencies: Now that we have our modules information in an AST, we can now “walk” the AST for specific types of statements and expressions. What we are looking for are what we define as dependency statements. Therefore, the
Parser“walks” the AST and comes across the AST information for a
require("foo")statement, then that information is stored in a
Dependencyinstance and is attached to that module.
- Repeat the process: Once all
Dependenciesfor that module have been found, we need to then “process” them. This is where the recursion occurs. Each dependency will need to go through these bullet points above to find the module it is pointing to.
With the information you have learned, pick one of the following Tapable instances mentioned above, and go to our core repository and find a plugin written for that instance. Observe how information and state flows between them and how the
Parser leverages events to create
I personally love using chrome://inspect and use the Dedicated Node Debugging Tools to help me step through breakpoints and complex pieces of the source code.
Starting from somewhere that is familiar and understandable and branching out is one of the best ways to dive into a new code base!! To stick with the theme of this article, my recommendation would be Compiler.js or Compilation.js!
Learning the source, writing your own plugins, asking questions, and/or take notes!! These are all incredible ways that you can not only build your own skills, but equip yourself to be the next webpack contributor!!! And at the end of the day, have fun!!
Stay subscribed to our medium publication and stay tuned for the next part in this series as we learn what webpack does with this graph to create the bundles we see.
No time to help contribute? Want to give back in other ways? Become a Backer or Sponsor to webpack by donating to our open collective. Open Collective not only helps support the Core Team, but also supports contributors who have spent significant time improving our organization on their free time! ❤