UNPKG

webpack

Version:

Packs ECMAScript/CommonJs/AMD modules for the browser. Allows you to split your codebase into multiple bundles, which can be loaded on demand. Supports loaders to preprocess files, i.e. json, jsx, es7, css, less, ... and your custom stuff.

125 lines (99 loc) • 3.67 kB
/* MIT License http://www.opensource.org/licenses/mit-license.php Author Alexander Akait @alexander-akait */ "use strict"; const CacheEffectivenessWarning = require("../errors/CacheEffectivenessWarning"); /** @import { PerformanceOptions } from "../../declarations/WebpackOptions" */ /** @import Compiler from "../Compiler" */ /** @import { BuildInfo } from "../module/Module" */ const PLUGIN_NAME = "CacheEffectivenessPlugin"; // A reason shared by many modules is the one worth fixing, so report the // frequent ones and count the rest. const MAX_REPORTED_REASONS = 3; const UNSPECIFIED_REASON = "reason not stated"; class CacheEffectivenessPlugin { /** * Creates an instance of CacheEffectivenessPlugin. * @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; if (!hints) return; compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => { compilation.hooks.afterSeal.tap(PLUGIN_NAME, () => { let total = 0; let rebuilt = 0; let uncacheable = 0; /** @type {Map<string, number>} */ const modulesByReason = new Map(); for (const module of compilation.modules) { // Runtime modules are generated per compilation, never built and // never cached, so counting them makes a cold build look warm. if (module.is("runtime")) continue; total++; if (compilation.builtModules.has(module)) rebuilt++; // Every built module carries one; `cacheable` being `undefined` is // the module never reporting either way, not it opting out. const buildInfo = /** @type {BuildInfo} */ (module.buildInfo); if (buildInfo.cacheable !== false) continue; uncacheable++; const notCacheableReasons = buildInfo.diagnostics && buildInfo.diagnostics.notCacheableReasons; const reasons = notCacheableReasons && notCacheableReasons.length > 0 ? notCacheableReasons : [UNSPECIFIED_REASON]; for (const reason of reasons) { modulesByReason.set(reason, (modulesByReason.get(reason) || 0) + 1); } } // Work redone despite a warm cache needs both ends: everything rebuilt // is a cold build, nothing rebuilt is the cache working. const warmRebuild = rebuilt > 0 && rebuilt < total; if (!warmRebuild && uncacheable === 0) return; // Most frequent first, then by reason: the map is keyed in walk order, // which differs between builds, and only three reasons are reported, so // an unbroken tie would vary which of them a build names. const sorted = [...modulesByReason].sort( (a, b) => b[1] - a[1] || (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0) ); const reasons = sorted .slice(0, MAX_REPORTED_REASONS) .map( ([reason, modules]) => `${reason} (${modules} ${modules === 1 ? "module" : "modules"})` ); const remaining = sorted.length - reasons.length; if (remaining > 0) { reasons.push( `and ${remaining} other ${remaining === 1 ? "reason" : "reasons"}` ); } const warning = new CacheEffectivenessWarning({ total, rebuilt, uncacheable, reasons }); if (hints === "error") { compilation.errors.push(warning); } else if (hints === "stats") { compilation.hints.push(warning); } else { compilation.warnings.push(warning); } }); }); } } module.exports = CacheEffectivenessPlugin;