InlinedAssetsPlugin.js 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Alexander Akait @alexander-akait
  4. */
  5. "use strict";
  6. const InlinedAssetsWarning = require("../errors/InlinedAssetsWarning");
  7. const { compareStrings } = require("../util/comparators");
  8. const getModuleSize = require("./getModuleSize");
  9. /** @import { AssetModuleBuildInfo } from "../asset/AssetModule" */
  10. /** @import { PerformanceOptions } from "../../declarations/WebpackOptions" */
  11. /** @import Compiler from "../Compiler" */
  12. /** @import { InlinedAssetDetails } from "../errors/InlinedAssetsWarning" */
  13. const PLUGIN_NAME = "InlinedAssetsPlugin";
  14. // Enough to name the offenders without listing every icon.
  15. const MAX_REPORTED_ASSETS = 5;
  16. // What the asset modules default `dataUrlCondition.maxSize` is: below it,
  17. // inlining saves a request for less than it costs in bytes.
  18. const DEFAULT_MAX_SIZE = 8096;
  19. class InlinedAssetsPlugin {
  20. /**
  21. * Creates an instance of InlinedAssetsPlugin.
  22. * @param {PerformanceOptions} options the plugin options
  23. */
  24. constructor(options) {
  25. /** @type {PerformanceOptions["hints"]} */
  26. this.hints = options.hints;
  27. }
  28. /**
  29. * Applies the plugin by registering its hooks on the compiler.
  30. * @param {Compiler} compiler the compiler instance
  31. * @returns {void}
  32. */
  33. apply(compiler) {
  34. const hints = this.hints;
  35. if (!hints) return;
  36. compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
  37. // `afterSeal` is past the hash, which folds every message into it — a
  38. // hint reported earlier would change the build's identity.
  39. compilation.hooks.afterSeal.tap(PLUGIN_NAME, () => {
  40. /** @type {InlinedAssetDetails[]} */
  41. const assets = [];
  42. let total = 0;
  43. for (const module of compilation.modules) {
  44. // The parser records whether it inlined, so this asks rather than
  45. // deciding from the module's type what it must have done.
  46. const buildInfo =
  47. /** @type {AssetModuleBuildInfo} */
  48. (module.buildInfo);
  49. if (!buildInfo.dataUrl) continue;
  50. // Module sizes are estimates and come back fractional.
  51. const size = Math.round(getModuleSize(module));
  52. if (size <= DEFAULT_MAX_SIZE) continue;
  53. total += size;
  54. assets.push({
  55. name: module.readableIdentifier(compilation.requestShortener),
  56. size
  57. });
  58. }
  59. if (assets.length === 0) return;
  60. // Largest first; ties break by name, module order is not stable.
  61. assets.sort(
  62. (a, b) => b.size - a.size || compareStrings(a.name, b.name)
  63. );
  64. const warning = new InlinedAssetsWarning(
  65. assets.slice(0, MAX_REPORTED_ASSETS),
  66. total
  67. );
  68. if (hints === "error") {
  69. compilation.errors.push(warning);
  70. } else if (hints === "stats") {
  71. compilation.hints.push(warning);
  72. } else {
  73. compilation.warnings.push(warning);
  74. }
  75. });
  76. });
  77. }
  78. }
  79. module.exports = InlinedAssetsPlugin;