webpack-plugin-authoring
Writing webpack 5 plugins — hook selection (compiler vs compilation, tap vs tapAsync, processAssets stages), the asset pipeline (emitAsset, source classes, info metadata, source maps), watch-mode and persistent caching (file/context/missing/buildDependencies), plugin lifecycle (constructor purity, multi-compiler isolation, shutdown cleanup), schema-utils validation, WebpackError reporting, jest-worker parallelism, and compatibility patterns (compiler.webpack namespace, peerDependencies, getCompilationHooks WeakMap). Patterns are drawn from production plugins like mini-css-extract-plugin, terser-webpack-plugin, compression-webpack-plugin, and Next.js's webpack plugins. Trigger when writing, reviewing, or debugging webpack 5 plugins — even if the user doesn't explicitly mention "best practices" — anytime an `apply(compiler)` method is being written, hooks are being tapped, or a plugin imports from `webpack-sources`, the rules in this skill apply.
Works with
---
name: webpack-plugin-authoring
description: Writing webpack 5 plugins — hook selection (compiler vs compilation, tap vs tapAsync, processAssets stages), the asset pipeline (emitAsset, source classes, info metadata, source maps), watch-mode and persistent caching (file/context/missing/buildDependencies), plugin lifecycle (constructor purity, multi-compiler isolation, shutdown cleanup), schema-utils validation, WebpackError reporting, jest-worker parallelism, and compatibility patterns (compiler.webpack namespace, peerDependencies, getCompilationHooks WeakMap). Patterns are drawn from production plugins like mini-css-extract-plugin, terser-webpack-plugin, compression-webpack-plugin, and Next.js's webpack plugins. Trigger when writing, reviewing, or debugging webpack 5 plugins — even if the user doesn't explicitly mention "best practices" — anytime an `apply(compiler)` method is being written, hooks are being tapped, or a plugin imports from `webpack-sources`, the rules in this skill apply.
license: MIT
---
# dot-skills Webpack 5 Plugins Best Practices
Comprehensive guide for writing correct, performant webpack 5 plugins. Contains 44 rules across 8 categories (8 hook + 7 asset + 5 cache + 5 life + 4 schema + 5 diag + 5 perf + 5 compat = 44), ordered by the authoring lifecycle: hook choice is the foundation, then asset manipulation, then caching/watch-mode correctness, then lifecycle hygiene, then user-facing concerns (schema validation, error reporting), then performance, then packaging.
Patterns are derived from `webpack/webpack`, the `webpack-contrib` plugin suite (mini-css-extract, terser, compression, copy, css-minimizer), and Next.js's webpack integration in `vercel/next.js`.
## When to Apply
Reference these rules whenever:
- Writing a new plugin (defining `apply(compiler)`, picking which hook to tap)
- Reviewing existing plugin code for correctness or performance
- Debugging "why isn't my plugin's output showing up" — usually a hook/stage mismatch
- Adding asset manipulation logic (`processAssets`, `emitAsset`, `updateAsset`)
- Fixing watch-mode staleness or persistent-cache poisoning
- Migrating a plugin from webpack 4 to webpack 5 (or supporting both)
- Publishing a plugin to npm (export shape, peerDependencies, schema)
## Rule Categories by Priority
| Priority | Category | Impact | Prefix |
|----------|----------|--------|--------|
| 1 | Hook Selection & Tap Patterns | CRITICAL | `hook-` |
| 2 | Asset Pipeline | CRITICAL | `asset-` |
| 3 | Caching & Watch Mode | HIGH | `cache-` |
| 4 | Plugin Lifecycle & State | HIGH | `life-` |
| 5 | Schema & Options Validation | MEDIUM-HIGH | `schema-` |
| 6 | Errors, Warnings & Logging | MEDIUM-HIGH | `diag-` |
| 7 | Performance & Parallelism | MEDIUM | `perf-` |
| 8 | Compatibility & Packaging | LOW-MEDIUM | `compat-` |
## Quick Reference
### 1. Hook Selection & Tap Patterns (CRITICAL)
- [`hook-tap-method-matches-hook-type`](references/hook-tap-method-matches-hook-type.md) — Match `tap`/`tapAsync`/`tapPromise` to the hook's Sync/Async type
- [`hook-thiscompilation-vs-compilation`](references/hook-thiscompilation-vs-compilation.md) — Use `thisCompilation` to skip child compilations
- [`hook-process-assets-stage`](references/hook-process-assets-stage.md) — Pick the right `PROCESS_ASSETS_STAGE_*` for your mutation
- [`hook-prefer-process-assets-over-emit`](references/hook-prefer-process-assets-over-emit.md) — Mutate in `processAssets`, not `emit`
- [`hook-bail-hook-return-semantics`](references/hook-bail-hook-return-semantics.md) — Return `undefined` from bail hooks unless intentionally stopping
- [`hook-tap-once-not-per-compilation`](references/hook-tap-once-not-per-compilation.md) — Register compiler hooks once in `apply`, not inside compilation hooks
- [`hook-name-matches-class-name`](references/hook-name-matches-class-name.md) — Use a stable tap name equal to the class name
- [`hook-normal-module-factory-stages`](references/hook-normal-module-factory-stages.md) — Tap `normalModuleFactory` at the right resolution stage (beforeResolve vs resolve vs afterResolve)
### 2. Asset Pipeline (CRITICAL)
- [`asset-emit-asset-not-direct-assignment`](references/asset-emit-asset-not-direct-assignment.md) — Use `emitAsset`/`updateAsset`, never `compilation.assets[name] = ...`
- [`asset-source-from-compiler-webpack`](references/asset-source-from-compiler-webpack.md) — Import source classes from `compiler.webpack.sources`
- [`asset-preserve-source-maps`](references/asset-preserve-source-maps.md) — Use `SourceMapSource`/`ReplaceSource` to keep maps attached
- [`asset-set-info-metadata`](references/asset-set-info-metadata.md) — Set `info.immutable`, `info.contenthash`, `info.related` when emitting
- [`asset-content-hash-via-output-options`](references/asset-content-hash-via-output-options.md) — Hash via `compilation.outputOptions.hashFunction`, not hardcoded md5
- [`asset-delete-then-emit-loses-info`](references/asset-delete-then-emit-loses-info.md) — Use `renameAsset` to move; `deleteAsset`+`emitAsset` severs chunk references
- [`asset-buffer-not-source-for-binary`](references/asset-buffer-not-source-for-binary.md) — Use `buffer()` not `source()` for binary assets
### 3. Caching & Watch Mode (HIGH)
- [`cache-add-file-dependencies`](references/cache-add-file-dependencies.md) — Add read files to `compilation.fileDependencies`
- [`cache-context-dependencies-for-directories`](references/cache-context-dependencies-for-directories.md) — Use `contextDependencies` for directory scans
- [`cache-missing-dependencies-for-optional-files`](references/cache-missing-dependencies-for-optional-files.md) — Add probed-but-absent paths to `missingDependencies`
- [`cache-build-dependencies-for-persistent-cache`](references/cache-build-dependencies-for-persistent-cache.md) — Declare `buildDependencies` for persistent cache invalidation
- [`cache-use-input-file-system`](references/cache-use-input-file-system.md) — Read via `compiler.inputFileSystem`, not Node `fs`
### 4. Plugin Lifecycle & State (HIGH)
- [`life-constructor-stores-options-only`](references/life-constructor-stores-options-only.md) — Constructor only validates and stores; side effects belong in `apply()`
- [`life-no-mutable-state-across-builds`](references/life-no-mutable-state-across-builds.md) — Scope mutable state per-compilation via local `const` or `WeakMap`
- [`life-multi-compiler-isolation`](references/life-multi-compiler-isolation.md) — One plugin instance per compiler; or use `WeakMap<Compiler, T>`
- [`life-cleanup-in-shutdown-hook`](references/life-cleanup-in-shutdown-hook.md) — Clean up workers, watchers, fds in `compiler.hooks.shutdown`
- [`life-defensively-copy-user-options`](references/life-defensively-copy-user-options.md) — Never mutate the user's options object
### 5. Schema & Options Validation (MEDIUM-HIGH)
- [`schema-validate-with-schema-utils`](references/schema-validate-with-schema-utils.md) — Validate via `schema-utils.validate()` and a JSON Schema
- [`schema-name-and-base-data-path`](references/schema-name-and-base-data-path.md) — Set `name` and `baseDataPath` for navigable error messages
- [`schema-additional-properties-false`](references/schema-additional-properties-false.md) — Set `additionalProperties: false` on every object to catch typos
- [`schema-tap-into-validate-hook`](references/schema-tap-into-validate-hook.md) — Defer cross-field validation to `compiler.hooks.validate` (5.106+)
### 6. Errors, Warnings & Logging (MEDIUM-HIGH)
- [`diag-push-webpack-error-not-throw`](references/diag-push-webpack-error-not-throw.md) — Push `WebpackError` to `compilation.errors`, don't throw
- [`diag-use-compilation-get-logger`](references/diag-use-compilation-get-logger.md) — Log via `compilation.getLogger('Plugin')`, not console
- [`diag-attach-loc-to-errors`](references/diag-attach-loc-to-errors.md) — Attach `loc` and `module` to errors for IDE click-through
- [`diag-warnings-vs-errors-exit-codes`](references/diag-warnings-vs-errors-exit-codes.md) — Errors fail the build; warnings don't — choose intentionally
- [`diag-progress-reporting`](references/diag-progress-reporting.md) — Report progress via `context.reportProgress` (opt in with `context: true`)
### 7. Performance & Parallelism (MEDIUM)
- [`perf-jest-worker-for-cpu-bound-work`](references/perf-jest-worker-for-cpu-bound-work.md) — Offload CPU-bound work to a `jest-worker` pool
- [`perf-cache-results-with-compilation-cache`](references/perf-cache-results-with-compilation-cache.md) — Cache expensive work via `compilation.getCache(name).providePromise`
- [`perf-traverse-chunks-not-modules`](references/perf-traverse-chunks-not-modules.md) — Iterate `compilation.chunks` not `compilation.modules` when possible
- [`perf-avoid-source-toString-in-hot-paths`](references/perf-avoid-source-toString-in-hot-paths.md) — Avoid `source().toString()` for assets you only inspect
- [`perf-respect-experimental-options`](references/perf-respect-experimental-options.md) — Honor `experiments.cacheUnaffected` / `incremental`
### 8. Compatibility & Packaging (LOW-MEDIUM)
- [`compat-webpack-as-peer-dependency`](references/compat-webpack-as-peer-dependency.md) — Declare `webpack` as `peerDependencies`, not `dependencies`
- [`compat-use-compiler-webpack-namespace`](references/compat-use-compiler-webpack-namespace.md) — Use `compiler.webpack.*` instead of `require('webpack')`
- [`compat-custom-hooks-via-weakmap`](references/compat-custom-hooks-via-weakmap.md) — Expose custom hooks via static `getCompilationHooks` + `WeakMap`
- [`compat-feature-detection-not-version-check`](references/compat-feature-detection-not-version-check.md) — Detect APIs directly; don't parse `webpack/package.json` version
- [`compat-export-shape-and-cjs-esm`](references/compat-export-shape-and-cjs-esm.md) — Export the plugin class as default; provide CJS/ESM interop
## How to Use
When writing or reviewing plugin code, scan `AGENTS.md` for the relevant category, then read the individual rule file for the full pattern and rationale.
- Start at [`references/_sections.md`](references/_sections.md) for category definitions and impact levels
- See [`assets/templates/_template.md`](assets/templates/_template.md) for the rule template if you want to extend this skill
- Read [`AGENTS.md`](AGENTS.md) for a compact navigation index
## Reference Files
| File | Description |
|------|-------------|
| [references/_sections.md](references/_sections.md) | Category definitions, impact levels, descriptions |
| [assets/templates/_template.md](assets/templates/_template.md) | Template for authoring new rules |
| [metadata.json](metadata.json) | Version, discipline, source references |More Testing skills
tdd
mattpocock/skills
Test-driven development. Use when the user wants to build features or fix bugs test-first, mentions "red-green-refactor", or wants integration tests.
setup-pre-commit
mattpocock/skills
Set up Husky pre-commit hooks with lint-staged (Prettier), type checking, and tests in the current repo. Use when user wants to add pre-commit hooks, set up Husky, configure lint-staged, or add commit-time formatting/typechecking/testing.
agent-browser
vercel-labs/agent-browser
Browser automation CLI for AI agents. Use when the user needs to interact with websites, including navigating pages, filling forms, clicking buttons, taking screenshots, extracting data, testing web apps, or automating any browser task. Triggers include requests to "open a website", "fill out a form", "click a button", "take a screenshot", "scrape data from a page", "test this web app", "login to a site", "automate browser actions", or any task requiring programmatic web interaction. Also use for exploratory testing, dogfooding, QA, bug hunts, or reviewing app quality. Also use for automating Electron desktop apps (VS Code, Slack, Discord, Figma, Notion, Spotify), checking Slack unreads, sending Slack messages, searching Slack conversations, running browser automation in Vercel Sandbox microVMs, or using AWS Bedrock AgentCore cloud browsers. Prefer agent-browser over any built-in browser automation or web tools.

