DynamicExportsPlugin.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Alexander Akait @alexander-akait
  4. */
  5. "use strict";
  6. const DynamicExportsWarning = require("../errors/DynamicExportsWarning");
  7. const { compareStrings } = require("../util/comparators");
  8. const getModuleSize = require("./getModuleSize");
  9. /** @import { PerformanceOptions } from "../../declarations/WebpackOptions" */
  10. /** @import Compiler from "../Compiler" */
  11. /** @import Module from "../Module" */
  12. /** @import ModuleGraph from "../ModuleGraph" */
  13. /** @import { DynamicExportsDetails } from "../errors/DynamicExportsWarning" */
  14. /**
  15. * Tells whether another module imports this one.
  16. * @param {Module} module the module to look up
  17. * @param {ModuleGraph} moduleGraph the module graph
  18. * @returns {boolean} true when at least one module imports it
  19. */
  20. const hasImporter = (module, moduleGraph) => {
  21. for (const connection of moduleGraph.getIncomingConnections(module)) {
  22. if (connection.originModule) return true;
  23. }
  24. return false;
  25. };
  26. const PLUGIN_NAME = "DynamicExportsPlugin";
  27. // Enough to name the offenders without listing every CommonJS dependency.
  28. const MAX_REPORTED_MODULES = 5;
  29. class DynamicExportsPlugin {
  30. /**
  31. * Creates an instance of DynamicExportsPlugin.
  32. * @param {PerformanceOptions} options the plugin options
  33. */
  34. constructor(options) {
  35. /** @type {PerformanceOptions["hints"]} */
  36. this.hints = options.hints;
  37. }
  38. /**
  39. * Applies the plugin by registering its hooks on the compiler.
  40. * @param {Compiler} compiler the compiler instance
  41. * @returns {void}
  42. */
  43. apply(compiler) {
  44. const hints = this.hints;
  45. if (!hints) return;
  46. compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
  47. /** @type {DynamicExportsWarning | undefined} */
  48. let warning;
  49. // Exports are settled here, and concatenation has not merged the
  50. // modules away yet, so each one can still be named and measured.
  51. compilation.hooks.optimizeModules.tap(PLUGIN_NAME, (modules) => {
  52. const { chunkGraph, moduleGraph, requestShortener } = compilation;
  53. /** @type {DynamicExportsDetails[]} */
  54. const unknown = [];
  55. for (const module of modules) {
  56. // One webpack already left out costs nothing.
  57. if (chunkGraph.getNumberOfModuleChunks(module) === 0) continue;
  58. // `null` is the third state: not "no other exports" but "there
  59. // may be more, and they cannot be read from the source".
  60. if (
  61. moduleGraph.getExportsInfo(module).otherExportsInfo.provided !==
  62. null
  63. ) {
  64. continue;
  65. }
  66. // The cost lands on whoever imports it, so an entry pays nothing.
  67. if (!hasImporter(module, moduleGraph)) continue;
  68. unknown.push({
  69. name: module.readableIdentifier(requestShortener),
  70. size: getModuleSize(module)
  71. });
  72. }
  73. if (unknown.length === 0) return;
  74. // Ties break by name: module order is not stable across runtimes.
  75. unknown.sort(
  76. (a, b) => b.size - a.size || compareStrings(a.name, b.name)
  77. );
  78. warning = new DynamicExportsWarning(
  79. unknown.slice(0, MAX_REPORTED_MODULES),
  80. unknown.length
  81. );
  82. });
  83. // Reported past the hash: `createHash` folds every message into it, so
  84. // a hint pushed earlier would change the build's identity.
  85. compilation.hooks.afterSeal.tap(PLUGIN_NAME, () => {
  86. if (warning === undefined) return;
  87. if (hints === "error") {
  88. compilation.errors.push(warning);
  89. } else if (hints === "stats") {
  90. compilation.hints.push(warning);
  91. } else {
  92. compilation.warnings.push(warning);
  93. }
  94. });
  95. });
  96. }
  97. }
  98. module.exports = DynamicExportsPlugin;