DiagnosticsWebpackPlugin
This plugin only supports webpack 5 and Node.js
>= 22.12.0.
This plugin runs linters, type checkers and other diagnostic tools over your sources during the webpack build and reports what they find as webpack errors and warnings.
It replaces eslint-webpack-plugin and stylelint-webpack-plugin: one plugin, one place to configure how problems are reported, and one pass over your project. Today it runs ESLint and Stylelint; more linters and diagnostic tools are meant to be added the same way.
Getting Started
To begin, you'll need to install diagnostics-webpack-plugin:
npm install diagnostics-webpack-plugin --save-dev
or
yarn add -D diagnostics-webpack-plugin
or
pnpm add -D diagnostics-webpack-plugin
[!NOTE]
Install the linters you want to run as well — the plugin only requires the ones you enable. It supports
eslint >= 9andstylelint >= 17:
npm install eslint stylelint --save-dev
Then add the plugin to your webpack configuration and enable a check for each language you want inspected:
import DiagnosticsPlugin from "diagnostics-webpack-plugin";
export default {
// ...
plugins: [
new DiagnosticsPlugin({
checks: [
{ use: "eslint", extensions: ["js", "mjs"] },
{ use: "stylelint", extensions: ["css", "scss"] },
],
}),
],
// ...
};The package ships an ECMAScript build next to a CommonJS one, so a CommonJS configuration works just as well:
const DiagnosticsPlugin = require("diagnostics-webpack-plugin");Options
The plugin options have three layers:
| Layer | Where it goes | What it covers |
|---|---|---|
| Plugin | Top level only | How the plugin schedules its work, for every check at once. |
| Shared | Top level or in a checks entry | Which files are linted and how problems are reported. An entry overrides what it sets. |
| Check | In a checks entry | Options only that tool understands, plus everything its own Node.js API accepts. |
Every check to run is an entry in checks, named by its use. The list may name the same tool more than once, so one instance can inspect two file sets under different configurations.
new DiagnosticsPlugin({
// Plugin options
context: "src",
// Shared options, every check uses them unless it says otherwise
failOnError: true,
exclude: ["node_modules", "vendor"],
// The checks to run, each with the options only it understands
checks: [
{ use: "eslint", extensions: ["js", "ts"], fix: true },
{ use: "stylelint", extensions: ["css", "scss"], threads: true },
],
});Plugin options
context
- Type:
type context = string;- Default:
compiler.context
Base directory for linting. Every relative files and exclude pattern is resolved against it.
lintDirtyModulesOnly
- Type:
type lintDirtyModulesOnly = boolean;- Default:
false
Lint only changed files, skipping the initial lint on build start.
Shared options
These can be set at the top level, where they apply to every check, or inside one check, where they apply to that check alone.
cache
- Type:
type cache = boolean;- Default:
true
The cache is enabled by default to decrease execution time.
cacheLocation
- Type:
type cacheLocation = string;- Default:
node_modules/.cache/diagnostics-webpack-plugin/.<tool>cache
Specify the path to the cache location. Can be a file or a directory.
files
- Type:
type files = string | string[];- Default:
options.context
Specify directories, files, or globs. Must be relative to options.context.
Directories are traversed recursively looking for files matching options.extensions.
File and glob patterns ignore options.extensions.
extensions
- Type:
type extensions = string | string[];- Default:
'js'for ESLint,['css', 'scss', 'sass']for Stylelint
Specify file extensions that should be checked.
exclude
- Type:
type exclude = string | string[];- Default:
'node_modules', plusoutput.pathfor Stylelint
Specify the files/directories to exclude. Must be relative to options.context.
resourceQueryExclude
- Type:
type resourceQueryExclude = RegExp | RegExp[];- Default:
[]
Specify the resource query to exclude. Only affects checks that read the module graph, such as ESLint.
fix
- Type:
type fix = boolean;- Default:
false
Will enable the autofix feature of the tool.
Be careful: this option will modify source files.
formatter
- Type:
type formatter = string | ((results: LintResult[]) => string);- Default: the tool's own default formatter
Accepts the name of a formatter the tool ships, or a function that receives its results and returns the output as a string.
See the ESLint formatters and the Stylelint formatter option.
Errors and warnings
Every check reports its errors as webpack errors and its warnings as webpack warnings. emitError and emitWarning choose what is reported at all, and failOnError and failOnWarning choose whether the build is failed over it.
emitError
- Type:
type emitError = boolean;- Default:
true
The errors found will always be emitted, to disable set to false.
emitWarning
- Type:
type emitWarning = boolean;- Default:
true
The warnings found will always be emitted, to disable set to false.
failOnError
- Type:
type failOnError = boolean;- Default:
true,falseindevelopmentmode
Will cause the module build to fail if any errors are found, to disable set to false.
failOnWarning
- Type:
type failOnWarning = boolean;- Default:
false
Will cause the module build to fail if any warnings are found, if set to true.
quiet
- Type:
type quiet = boolean;- Default:
false
Will process and report errors only and ignore warnings, if set to true.
outputReport
- Type:
type outputReport =
| boolean
| {
filePath?: string | undefined;
formatter?: (string | ((results: LintResult[]) => string)) | undefined;
};- Default:
false
Write the results to a file, for example a checkstyle xml file for use for reporting on Jenkins CI.
filePath: path to the output report file, relative tooutput.pathunless absolute.formatter: a differentformatterfor the output file; the default/configured formatter is used when none is passed in.
Set at the top level, every check appends its report to the same file. Set it inside a checks entry to give that check a file of its own.
new DiagnosticsPlugin({
checks: [
{
use: "eslint",
outputReport: { filePath: "eslint.json", formatter: "json" },
},
{
use: "stylelint",
outputReport: { filePath: "stylelint.json", formatter: "json" },
},
],
});ESLint
Run with { use: "eslint" }. It lints the files webpack builds, so only the modules that end up in the bundle are checked.
Alongside the shared options you can pass any ESLint Node.js API option — they are handed to the ESLint class as they are.
configType
- Type:
type configType = "flat" | "eslintrc";- Default:
flat
Specify the type of configuration to use with ESLint.
flatis the current standard configuration format.eslintrcis the legacy configuration format and has been officially deprecated.
The new configuration format is explained in its own documentation.
eslintPath
- Type:
type eslintPath = string;- Default:
eslint
Path to the eslint instance that will be used for linting.
If the eslintPath is a folder like the official ESLint, or you specify a formatter option, you don't have to install eslint.
Suppressions
Bulk suppressions are supported: enable ESLint's own applySuppressions, and point suppressionsLocation at the file if it is not the default eslint-suppressions.json.
new DiagnosticsPlugin({
checks: [{ use: "eslint", applySuppressions: true }],
});[!IMPORTANT]
ESLint resolves the suppressions file, and every path recorded inside it, against its own
cwd— not against the plugin'scontext. Where the two differ, passcwdto the check as well:new DiagnosticsPlugin({ context: "src", checks: [ { use: "eslint", applySuppressions: true, cwd: import.meta.dirname }, ], });
Suppressions need ESLint 9.24 or later. ESLint 10 takes both options itself; below that they reach its CLI alone, so the plugin applies the suppressions after linting instead — the same file, the same paths, the same result.
Stylelint
Run with { use: "stylelint" }, and requires stylelint >= 17. It lints every file matching files and extensions on disk, whether or not webpack imported it, so a stylesheet nothing imports yet is still checked.
Alongside the shared options you can pass any Stylelint option — they are handed to stylelint.lint() as they are.
stylelintPath
- Type:
type stylelintPath = string;- Default:
stylelint
Path to the stylelint instance that will be used for linting.
threads
- Type:
type threads = boolean | number;- Default:
false
Set to true for an auto-selected pool size based on the number of CPUs. Set to a number greater than 1 to set an explicit pool size.
Set to false, 1, or less to disable and only run in the main process.
Adding a check
A use may also be an adapter of its own rather than a built-in name, so a check can ship as its own package without an entry in this one:
new DiagnosticsPlugin({
checks: [
{ use: require("diagnostics-webpack-plugin-typescript"), strict: true },
],
});Such an adapter is an object with a name, and a create returning the five functions the plugin drives it through — what to lint, what came back, which results are errors and which warnings, how to format them, and what to release afterwards:
module.exports = {
name: "made-up",
// "modules" lints the files webpack built, "glob" every file matching `files`
filesSource: "glob",
// Merged under the options the user passes, and under the shared options
defaults: { extensions: ["ts"] },
async create({ key, options, compilation }) {
return {
lintFiles: async (files) => runTheTool(files),
getResults: async (results) => results,
splitResults: (results) => ({ errors: results, warnings: [] }),
getFormatter: async (formatter) => async (results) => format(results),
cleanup: async () => {},
};
},
};label, filesSource, defaults, defaultExclude and schema are optional; the plugin fills in the defaults of a module-scanning check that excludes node_modules.
Migrating
From eslint-webpack-plugin
Move the options you were passing into a checks entry:
-const ESLintPlugin = require("eslint-webpack-plugin");
+const DiagnosticsPlugin = require("diagnostics-webpack-plugin");
module.exports = {
plugins: [
- new ESLintPlugin({ extensions: ["js"], fix: true }),
+ new DiagnosticsPlugin({
+ checks: [{ use: "eslint", extensions: ["js"], fix: true }],
+ }),
],
};The shared options — context, files, exclude, failOnError and the rest of Errors and warnings — may stay at the top level instead. Everything else behaves as it did, and the default cacheLocation moved to node_modules/.cache/diagnostics-webpack-plugin/.eslintcache.
From stylelint-webpack-plugin
Move the options you were passing into a checks entry:
-const StylelintPlugin = require("stylelint-webpack-plugin");
+const DiagnosticsPlugin = require("diagnostics-webpack-plugin");
module.exports = {
plugins: [
- new StylelintPlugin({ extensions: ["css"], threads: true }),
+ new DiagnosticsPlugin({
+ checks: [{ use: "stylelint", extensions: ["css"], threads: true }],
+ }),
],
};Three things changed beyond the option shape:
- Stylelint 17 or later is required.
stylelint-webpack-pluginaccepted13through17; the merged plugin drops the older majors rather than carrying their compatibility branches forward. Stylelint 17 itself needs Node>= 20.19. - Errors and warnings are no longer swapped. Errors are reported as webpack errors and warnings as webpack warnings, whatever
failOnErrorandfailOnWarningsay; those two now decide whether the build is failed, not how a problem is reported. PreviouslyfailOnError: falseturned errors into warnings, andfailOnWarning: trueturned warnings into errors. failOnErrordefaults tofalseindevelopmentmode, matching the rest of the plugin, rather than beingtrueeverywhere.
The default cacheLocation moved to node_modules/.cache/diagnostics-webpack-plugin/.stylelintcache.
Running both
The two plugins become one instance, and options they had in common are written once:
module.exports = {
plugins: [
- new ESLintPlugin({ context: "src", failOnError: true, extensions: ["js"] }),
- new StylelintPlugin({ context: "src", failOnError: true, extensions: ["css"] }),
+ new DiagnosticsPlugin({
+ context: "src",
+ failOnError: true,
+ checks: [
+ { use: "eslint", extensions: ["js"] },
+ { use: "stylelint", extensions: ["css"] },
+ ],
+ }),
],
};Changelog
Contributing
We welcome all contributions!
If you're new here, please take a moment to review our contributing guidelines.



