ConflictingResourceHintsPlugin.js 2.7 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 ConflictingResourceHintsWarning = require("../errors/ConflictingResourceHintsWarning");
  7. const { compareStrings } = require("../util/comparators");
  8. /** @import { PerformanceOptions } from "../../declarations/WebpackOptions" */
  9. /** @import ChunkGroup from "../ChunkGroup" */
  10. /** @import Compiler from "../Compiler" */
  11. /**
  12. * @import {
  13. * ConflictingResourceHintDetails
  14. * } from "../errors/ConflictingResourceHintsWarning"
  15. */
  16. const PLUGIN_NAME = "ConflictingResourceHintsPlugin";
  17. /**
  18. * Names a chunk group for a message, falling back to its chunks: an `import()`
  19. * without `webpackChunkName` produces a group with no name of its own.
  20. * @param {ChunkGroup} chunkGroup a chunk group
  21. * @returns {string} a name for it
  22. */
  23. const getChunkGroupName = (chunkGroup) =>
  24. chunkGroup.name ||
  25. chunkGroup.chunks.map((chunk) => chunk.name || `${chunk.id}`).join(", ");
  26. class ConflictingResourceHintsPlugin {
  27. /**
  28. * Creates an instance of ConflictingResourceHintsPlugin.
  29. * @param {PerformanceOptions} options the plugin options
  30. */
  31. constructor(options) {
  32. /** @type {PerformanceOptions["hints"]} */
  33. this.hints = options.hints;
  34. }
  35. /**
  36. * Applies the plugin by registering its hooks on the compiler.
  37. * @param {Compiler} compiler the compiler instance
  38. * @returns {void}
  39. */
  40. apply(compiler) {
  41. const hints = this.hints;
  42. compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
  43. compilation.hooks.afterSeal.tap(PLUGIN_NAME, () => {
  44. const { chunkGraph, moduleGraph } = compilation;
  45. /** @type {ConflictingResourceHintDetails[]} */
  46. const links = [];
  47. for (const chunkGroup of compilation.chunkGroups) {
  48. const children = chunkGroup.getChildrenByOrders(
  49. moduleGraph,
  50. chunkGraph
  51. );
  52. if (!children.prefetch || !children.preload) continue;
  53. const preloaded = new Set(children.preload);
  54. for (const child of children.prefetch) {
  55. if (!preloaded.has(child)) continue;
  56. links.push({
  57. parent: getChunkGroupName(chunkGroup),
  58. child: getChunkGroupName(child)
  59. });
  60. }
  61. }
  62. if (links.length === 0) return;
  63. // Chunk group iteration order is not stable across runs.
  64. links.sort(
  65. (a, b) =>
  66. compareStrings(a.parent, b.parent) ||
  67. compareStrings(a.child, b.child)
  68. );
  69. const warning = new ConflictingResourceHintsWarning(links);
  70. if (hints === "error") {
  71. compilation.errors.push(warning);
  72. } else if (hints === "stats") {
  73. compilation.hints.push(warning);
  74. } else {
  75. compilation.warnings.push(warning);
  76. }
  77. });
  78. });
  79. }
  80. }
  81. module.exports = ConflictingResourceHintsPlugin;