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— Matchtap/tapAsync/tapPromiseto the hook's Sync/Async typehook-thiscompilation-vs-compilation— UsethisCompilationto skip child compilationshook-process-assets-stage— Pick the rightPROCESS_ASSETS_STAGE_*for your mutationhook-prefer-process-assets-over-emit— Mutate inprocessAssets, notemithook-bail-hook-return-semantics— Returnundefinedfrom bail hooks unless intentionally stoppinghook-tap-once-not-per-compilation— Register compiler hooks once inapply, not inside compilation hookshook-name-matches-class-name— Use a stable tap name equal to the class namehook-normal-module-factory-stages— TapnormalModuleFactoryat the right resolution stage (beforeResolve vs resolve vs afterResolve)
2. Asset Pipeline (CRITICAL)
asset-emit-asset-not-direct-assignment— UseemitAsset/updateAsset, nevercompilation.assets[name] = ...asset-source-from-compiler-webpack— Import source classes fromcompiler.webpack.sourcesasset-preserve-source-maps— UseSourceMapSource/ReplaceSourceto keep maps attachedasset-set-info-metadata— Setinfo.immutable,info.contenthash,info.relatedwhen emittingasset-content-hash-via-output-options— Hash viacompilation.outputOptions.hashFunction, not hardcoded md5asset-delete-then-emit-loses-info— UserenameAssetto move;deleteAsset+emitAssetsevers chunk referencesasset-buffer-not-source-for-binary— Usebuffer()notsource()for binary assets
3. Caching & Watch Mode (HIGH)
cache-add-file-dependencies— Add read files tocompilation.fileDependenciescache-context-dependencies-for-directories— UsecontextDependenciesfor directory scanscache-missing-dependencies-for-optional-files— Add probed-but-absent paths tomissingDependenciescache-build-dependencies-for-persistent-cache— DeclarebuildDependenciesfor persistent cache invalidationcache-use-input-file-system— Read viacompiler.inputFileSystem, not Nodefs
4. Plugin Lifecycle & State (HIGH)
life-constructor-stores-options-only— Constructor only validates and stores; side effects belong inapply()life-no-mutable-state-across-builds— Scope mutable state per-compilation via localconstorWeakMaplife-multi-compiler-isolation— One plugin instance per compiler; or useWeakMap<Compiler, T>life-cleanup-in-shutdown-hook— Clean up workers, watchers, fds incompiler.hooks.shutdownlife-defensively-copy-user-options— Never mutate the user's options object
5. Schema & Options Validation (MEDIUM-HIGH)
schema-validate-with-schema-utils— Validate viaschema-utils.validate()and a JSON Schemaschema-name-and-base-data-path— SetnameandbaseDataPathfor navigable error messagesschema-additional-properties-false— SetadditionalProperties: falseon every object to catch typosschema-tap-into-validate-hook— Defer cross-field validation tocompiler.hooks.validate(5.106+)
6. Errors, Warnings & Logging (MEDIUM-HIGH)
diag-push-webpack-error-not-throw— PushWebpackErrortocompilation.errors, don't throwdiag-use-compilation-get-logger— Log viacompilation.getLogger('Plugin'), not consolediag-attach-loc-to-errors— Attachlocandmoduleto errors for IDE click-throughdiag-warnings-vs-errors-exit-codes— Errors fail the build; warnings don't — choose intentionallydiag-progress-reporting— Report progress viacontext.reportProgress(opt in withcontext: true)
7. Performance & Parallelism (MEDIUM)
perf-jest-worker-for-cpu-bound-work— Offload CPU-bound work to ajest-workerpoolperf-cache-results-with-compilation-cache— Cache expensive work viacompilation.getCache(name).providePromiseperf-traverse-chunks-not-modules— Iteratecompilation.chunksnotcompilation.moduleswhen possibleperf-avoid-source-toString-in-hot-paths— Avoidsource().toString()for assets you only inspectperf-respect-experimental-options— Honorexperiments.cacheUnaffected/incremental
8. Compatibility & Packaging (LOW-MEDIUM)
compat-webpack-as-peer-dependency— DeclarewebpackaspeerDependencies, notdependenciescompat-use-compiler-webpack-namespace— Usecompiler.webpack.*instead ofrequire('webpack')compat-custom-hooks-via-weakmap— Expose custom hooks via staticgetCompilationHooks+WeakMapcompat-feature-detection-not-version-check— Detect APIs directly; don't parsewebpack/package.jsonversioncompat-export-shape-and-cjs-esm— 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.mdfor category definitions and impact levels - See
assets/templates/_template.mdfor the rule template if you want to extend this skill - Read
AGENTS.mdfor a compact navigation index
Reference Files
| File | Description |
|---|---|
| references/_sections.md | Category definitions, impact levels, descriptions |
| assets/templates/_template.md | Template for authoring new rules |
| metadata.json | Version, discipline, source references |