webpack/webpack
The public surface is `lib/index.js` — a lazily-evaluated getter object exporting the `webpack()` factory plus ~100 plugin/class names and namespaced sub-objects (`webpack.cli`, `webpack.javascript.syntax`, `webpack.container`, `webpack.sharing`). `lib/webpack.js` normalizes, defaults and validates options, constructs a `Compiler`, and hands off to `WebpackOptionsApply`, which is the single place that translates resolved options into the ~60 plugins a build implies. Runtime boundaries are the filesystem (`lib/fs/fs.js` declares `InputFileSystem`/`OutputFileSystem`, `NodeEnvironmentPlugin` supplies the Node implementations), the loader sandbox (`lib/loaders/LoaderRunner.js`), and optional network I/O (`lib/schemes/HttpUriPlugin.js`, `lib/hmr/lazyCompilationBackend.js`). Persistence is the filesystem cache pack under `lib/cache/` plus the custom binary serializer in `lib/serialization/`. Build/test is all yarn scripts in `package.json`, and CI is 13 GitHub Actions workflows with `test.yml` as the hub.
- `lib/index.js` is the public export surface: every entry is a memoized getter so `require("webpack")` does not eagerly load the ~770 files under `lib/` — `webpack.cli` and `webpack.util.terminalColors` are split specifically so reading a terminal color does not pull in the options schema.
- The `webpack(options, callback)` factory in `lib/webpack.js` runs a precompiled schema check (`schemas/WebpackOptions.check`) first and only falls back to the full `validateSchema` (emitting a `DEP_WEBPACK_PRE_COMPILED_SCHEMA_INVALID` deprecation) when the fast check disagrees; an array config routes to `createMultiCompiler`.
- `createCompiler` fixes the startup order: `getNormalizedWebpackOptions` → `applyWebpackOptionsBaseDefaults` → `new Compiler` → `NodeEnvironmentPlugin` → user plugins → `applyWebpackOptionsDefaults` → `environment`/`afterEnvironment` hooks → `WebpackOptionsApply.process` → `initialize` hook. User plugins are therefore applied *before* the mode/target-dependent defaults are filled in.
- `lib/config/WebpackOptionsApply.js` (1,274 lines) is the option→plugin translation layer: it statically requires the always-on plugins (`JavascriptModulesPlugin`, `APIPlugin`, `ConstPlugin`, `RuntimePlugin`, `DefaultStatsFactoryPlugin`, …) and lazily requires target-specific ones (`NodeTargetPlugin`, `DenoTargetPlugin`, `BunTargetPlugin`, `ElectronTargetPlugin`, `EnableChunkLoadingPlugin`) inside the branches that need them.
- `lib/config/target.js` holds the target preset table: `TARGETS` is a list of `[name, description, regExp, handler]` tuples covering `browserslist[:…]`, `web`, `webworker`, `node[X.Y]`, `async-node`, `electron[X.Y]-main/preload/renderer`, `nwjs`/`node-webkit`, `deno`, `bun`, and `getTargetProperties` throws a message enumerating all of them for an unknown target.
- The storage layer is a two-tier cache: `MemoryCachePlugin`/`MemoryWithGcCachePlugin` for in-process, and `IdleFileCachePlugin` wrapping `PackFileCacheStrategy` (1,893 lines, with `Pack`, `PackContent` and `PackContainer` classes) for `cache.type: "filesystem"`, storing on an idle timer after the compiler goes idle.
- Cache persistence rides on a hand-written binary serializer in `lib/serialization/` (`BinaryMiddleware`, `ObjectMiddleware`, `FileMiddleware`, plus per-type serializers for Map/Set/RegExp/Date/Error); classes opt in through `makeSerializable(Class, "webpack/lib/…")`, used across 144 files in `lib/`.
- Deserialization resolves a class by request prefix: `ObjectMiddleware.registerLoader(/^webpack\/lib\//, …)` looks the suffix up in the generated `lib/util/internalSerializables.js` map and warns `<req> not found in internalSerializables` when a class was moved without regenerating — which is why `yarn fix:serializables` exists.
- External integrations are deliberately few: runtime dependencies are `tapable` (hooks), `enhanced-resolve` (resolution), `watchpack` (watching), `webpack-sources` (source objects), `terser` + `minimizer-webpack-plugin` (minification worker pool), `@webassemblyjs/*` (wasm), `browserslist`, `es-module-lexer`, `mime-db`, `schema-utils` and `graceful-fs`.
- All commands are yarn scripts: `yarn fix` (`fix:code` + `fix:special` + `fmt`), `yarn lint` (a nine-stage `&&` chain ending in `fmt:check` and `cspell`), `yarn test:base` (the only sanctioned jest wrapper — it carries `--experimental-vm-modules --expose-gc --max-old-space-size=4096`), and `yarn fix:special`, which runs all seven generators.
- Code generation is a first-class build step: `tooling/generate-types.js` reads `schemas/**/*.json` through Ajv `standaloneCode` and `json-schema-to-typescript` to emit precompiled validators, `declarations/*.d.ts` and `types.d.ts`, while `generate-{css,html,js}-data.js` derive the parser/minifier lookup tables from `mdn-data`, `@webref/*` and the pinned `acorn`.
- CI is 13 workflows; `test.yml` gates everything expensive behind three cheap jobs (`integration: needs: [lint, basic, unit]`, same for `test262`, `parser`, `syntax-equivalence`, the Deno/Bun runtime matrix and the benchmark shards), and the integration matrix spans Node 10.x through current with sharded `cover:integration:a|b` uploads to Codecov.
- Deployment shape is an npm package, not a service: `files` publishes only `lib/`, `bin/`, `hot/`, `schemas/`, `module.d.ts` and `types.d.ts`, releases are cut from `.changeset/` entries via `yarn version:release`, and `release.yml` / `publish-to-pkg-pr-new.yml` handle publishing and PR preview builds.
- `hot/` is a separate shipped surface — browser-side HMR runtime (`dev-server.js`, `poll.js`, `signal.js`, `log-apply-result.js`) plus the three lazy-compilation clients (`lazy-compilation-node.js`, `-web.js`, `-universal.js`) — type-checked by its own `tsconfig.hot.json` rather than the `lib/` project.