| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105 |
- /*
- MIT License http://www.opensource.org/licenses/mit-license.php
- Author Alexander Akait @alexander-akait
- */
- "use strict";
- const UnusedRuleWarning = require("../errors/UnusedRuleWarning");
- /**
- * @import {
- * RuleSetRule,
- * PerformanceOptions
- * } from "../../declarations/WebpackOptions"
- */
- /** @import Compiler from "../Compiler" */
- const PLUGIN_NAME = "UnusedRuleWarningPlugin";
- // `NormalModuleFactory` compiles `[{ rules: defaultRules }, { rules: rules }]`,
- // so only this subtree belongs to the configuration.
- const USER_RULES_PATH = "ruleSet[1].rules";
- /**
- * Names the rule the way its author would recognize it. `module.rules[7]` alone
- * is useless when a plugin injected the rule.
- * @param {RuleSetRule} rule the raw rule
- * @returns {string} a short descriptor, or an empty string when nothing identifies it
- */
- const describeRule = (rule) => {
- for (const key of ["test", "include", "exclude", "resource", "mimetype"]) {
- const value = rule[/** @type {keyof RuleSetRule} */ (key)];
- if (value instanceof RegExp) return ` (${key}: ${value})`;
- if (typeof value === "string") return ` (${key}: ${value})`;
- }
- if (typeof rule.loader === "string") return ` (loader: ${rule.loader})`;
- if (typeof rule.type === "string") return ` (type: ${rule.type})`;
- return "";
- };
- class UnusedRuleWarningPlugin {
- /**
- * Creates an instance of UnusedRuleWarningPlugin.
- * @param {PerformanceOptions} options the plugin options
- */
- constructor(options) {
- /** @type {PerformanceOptions["hints"]} */
- this.hints = options.hints;
- }
- /**
- * Applies the plugin by registering its hooks on the compiler.
- * @param {Compiler} compiler the compiler instance
- * @returns {void}
- */
- apply(compiler) {
- const hints = this.hints;
- compiler.hooks.compilation.tap(
- PLUGIN_NAME,
- (compilation, { normalModuleFactory }) => {
- compilation.hooks.afterSeal.tap(PLUGIN_NAME, () => {
- // Nothing was factorized, so no rule could match — an empty build is
- // not evidence that a rule is unused.
- if (compilation.modules.size === 0) return;
- /** @type {string[]} */
- const descriptions = [];
- for (const rule of normalModuleFactory.ruleSet.unusedRules()) {
- if (!rule.path.startsWith(USER_RULES_PATH)) continue;
- // A rule aimed at another compiler of a MultiCompiler is inactive
- // here by design, not by mistake.
- if (
- rule.conditions.some(
- (condition) => condition.property === "compiler"
- )
- ) {
- continue;
- }
- descriptions.push(
- `${rule.path.replace(USER_RULES_PATH, "module.rules")}${describeRule(
- rule.raw
- )}`
- );
- }
- if (descriptions.length === 0) return;
- const warning = new UnusedRuleWarning(descriptions);
- if (hints === "error") {
- compilation.errors.push(warning);
- } else if (hints === "stats") {
- compilation.hints.push(warning);
- } else {
- compilation.warnings.push(warning);
- }
- });
- }
- );
- }
- }
- module.exports = UnusedRuleWarningPlugin;
|