SplitChunksCappedPlugin.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 SplitChunksCappedWarning = require("../errors/SplitChunksCappedWarning");
  7. const { getCappedSplits } = require("../optimize/SplitChunksPlugin");
  8. const { compareStrings } = require("../util/comparators");
  9. /** @import { PerformanceOptions } from "../../declarations/WebpackOptions" */
  10. /** @import Compiler from "../Compiler" */
  11. /** @import { CappedSplitDetails } from "../errors/SplitChunksCappedWarning" */
  12. const PLUGIN_NAME = "SplitChunksCappedPlugin";
  13. // Enough to name the offenders without printing the chunk graph.
  14. const MAX_REPORTED_SPLITS = 5;
  15. class SplitChunksCappedPlugin {
  16. /**
  17. * Creates an instance of SplitChunksCappedPlugin.
  18. * @param {PerformanceOptions} options the plugin options
  19. */
  20. constructor(options) {
  21. /** @type {PerformanceOptions["hints"]} */
  22. this.hints = options.hints;
  23. }
  24. /**
  25. * Applies the plugin by registering its hooks on the compiler.
  26. * @param {Compiler} compiler the compiler instance
  27. * @returns {void}
  28. */
  29. apply(compiler) {
  30. const hints = this.hints;
  31. if (!hints) return;
  32. compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
  33. compilation.hooks.afterSeal.tap(PLUGIN_NAME, () => {
  34. const recorded = getCappedSplits(compilation);
  35. if (recorded === undefined || recorded.length === 0) return;
  36. /** @type {Map<string, CappedSplitDetails>} */
  37. const splits = new Map();
  38. for (const split of recorded) {
  39. // Chunk ids only exist after seal, and the queue reconsiders a
  40. // refused split, so the same one is recorded several times.
  41. const chunk = split.chunk.name || `${split.chunk.id}`;
  42. const key = `${split.cacheGroup} ${chunk} ${split.limit}`;
  43. const previous = splits.get(key);
  44. if (previous !== undefined && previous.modules >= split.modules) {
  45. continue;
  46. }
  47. splits.set(key, {
  48. cacheGroup: split.cacheGroup,
  49. chunk,
  50. limit: split.limit,
  51. maxRequests: split.maxRequests,
  52. modules: split.modules
  53. });
  54. }
  55. // Held only to be reported: nothing needs the chunks after this.
  56. recorded.length = 0;
  57. const details = [...splits.values()];
  58. // Ties break by name: the order splits are refused in is not stable.
  59. details.sort(
  60. (a, b) =>
  61. b.modules - a.modules ||
  62. compareStrings(a.cacheGroup, b.cacheGroup) ||
  63. compareStrings(a.chunk, b.chunk)
  64. );
  65. const warning = new SplitChunksCappedWarning(
  66. details.slice(0, MAX_REPORTED_SPLITS)
  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 = SplitChunksCappedPlugin;