Codebase Overview

This page is a map of webpack/webpack for someone about to change it. It does not cover how to open a pull request, which is in CONTRIBUTING.md, or how to run the suites, which is in TESTING_DOCS.md.

Five objects

Almost everything in lib/ hangs off one of these, and knowing which one owns a behavior is most of the work of finding its code.

ObjectWhat it is
CompilerCreated once from your configuration; drives the whole build and owns the filesystem and the watcher
CompilationCreated once per build; holds the module graph and the chunks, and is thrown away afterwards
ModuleOne thing that was built — a file, an asset, an external — and the source it generates
DependencyOne reference from a module to another, plus the template that rewrites it in the output
ChunkA group of modules that get emitted into one file

A run then has three phases, and each is a good place to put a debugger:

  1. makeCompiler#compile creates the compilation and adds the entries. Each entry is resolved, loaded through its loaders, parsed, and its dependencies are queued, until the graph stops growing.
  2. sealCompilation#seal turns that graph into output: chunks are formed, ids and hashes assigned, optimizations run, and code is generated.
  3. emitCompiler#emitAssets writes the assets to the output filesystem.

Every phase is a sequence of tapable hooks, which is how plugins get in. Compiler-wide hooks are declared at the top of Compiler.js and documented under Compiler Hooks; per-build hooks live in Compilation.js and are documented under Compilation Hooks.

Where the code lives

lib/ is CommonJS only, and its types are declared in JSDoc and compiled into types.d.ts — so a type error is fixed in the JSDoc above the function, never in the generated file.

AreaDirectories
The graphdependencies/ (dependency classes and their templates), optimize/ (SplitChunksPlugin, module concatenation), ids/
Languages and assetsjavascript/ (acorn parsing, exports analysis, code generation), css/, html/, json/, asset/, wasm*/, typescript/
Targetsweb/, webworker/, node/, deno/, electron/, bun/, esm/ — each with its own chunk loading and externals preset
Runtime shipped to usersruntime/ (the runtime modules webpack writes into a bundle) and hot/ at the repo root (the browser-side HMR client)
Configurationconfig/ (defaults, normalization, target presets), rules/ (the module.rules matching engine)
Featurescontainer/ and sharing/ (Module Federation), library/, hmr/, dll/, prefetch/, performance/, schemes/
Infrastructurecache/, serialization/ (persistent cache), logging/, stats/, errors/ (every error and warning class), util/

Outside lib/: schemas/ holds the JSON schemas that define the configuration API, bin/ the CLI entry point, tooling/ the code generators and analysis scripts, test/ the suites, and examples/ runnable configurations that double as documentation.

Finding the code behind a thing

  • A configuration option. Options cross four layers in order: its schema in schemas/WebpackOptions.json, its default in lib/config/defaults.js, any shorthand it accepts in lib/config/normalization.js, and finally the plugin that reads it. Grep the option name and you will land in all four.
  • A hook. Hooks are declared on the class that owns them, so search Compiler.js or Compilation.js for the name; lib/WebpackOptionsApply.js is where options get turned into the plugins that tap them.
  • Something in the generated bundle. Runtime symbols are named in lib/RuntimeGlobals.js; the code behind each one is a RuntimeModule subclass under lib/runtime/.
  • An error message. Error and warning classes live in lib/errors/, whatever pushes them.

Before you change it

  • Regenerate what is generated. types.d.ts, the files under declarations/, and the precompiled schema validators are all produced by yarn fix:special — edit the JSDoc or the schema and re-run it, never the output.
  • Cover the change with an integration test. test/configCases/ runs a real build and executes the bundle, which catches what a mocked unit test does not. See TESTING_DOCS.md for the layout and how to run a single case.
  • Add a changeset. Every user-facing change needs one file in .changeset/, which is what writes the changelog.
Edit this page·

1 Contributor

hai-x