DuplicatePackagesWarning.js 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Alexander Akait @alexander-akait
  4. */
  5. "use strict";
  6. const WebpackError = require("../errors/WebpackError");
  7. /** @import Module from "../Module" */
  8. /** @import ModuleGraph from "../ModuleGraph" */
  9. /** @import NormalModule from "../NormalModule" */
  10. /** @import RequestShortener from "../RequestShortener" */
  11. /** @import { PackageCopy } from "./DuplicatePackagesPlugin" */
  12. /**
  13. * @param {Module} module the module
  14. * @returns {string | undefined} directory of the package the module belongs to
  15. */
  16. const getPackagePath = (module) => {
  17. const resolveData =
  18. /** @type {NormalModule} */
  19. (module).resourceResolveData;
  20. return resolveData && resolveData.descriptionFileRoot;
  21. };
  22. /**
  23. * Finds a module outside of the copy that pulled it into the build, so the
  24. * warning can name the dependency responsible for this copy.
  25. * @param {PackageCopy} copy the copy
  26. * @param {ModuleGraph} moduleGraph the module graph
  27. * @returns {Module | undefined} a module importing this copy, when there is one
  28. */
  29. const findImporter = (copy, moduleGraph) => {
  30. for (const module of copy.modules) {
  31. for (const origin of moduleGraph
  32. .getIncomingConnectionsByOriginModule(module)
  33. .keys()) {
  34. if (origin && getPackagePath(origin) !== copy.path) return origin;
  35. }
  36. }
  37. };
  38. /**
  39. * Warning emitted when one package is included more than once in a build,
  40. * either in different versions or as several copies of the same version.
  41. */
  42. class DuplicatePackagesWarning extends WebpackError {
  43. /**
  44. * @param {string} name name of the package
  45. * @param {PackageCopy[]} copies copies of the package, sorted by path
  46. * @param {ModuleGraph} moduleGraph the module graph
  47. * @param {RequestShortener} requestShortener the request shortener
  48. */
  49. constructor(name, copies, moduleGraph, requestShortener) {
  50. const versions = new Set(copies.map((copy) => copy.version));
  51. const headline =
  52. versions.size > 1
  53. ? `Multiple versions of the package "${name}" (${[...versions].join(", ")}) are included in this build:`
  54. : `The package "${name}" (${copies[0].version}) is included ${copies.length} times in this build:`;
  55. const copiesList = copies
  56. .map((copy) => {
  57. const importer = findImporter(copy, moduleGraph);
  58. let message = `* ${copy.version} from ${requestShortener.shorten(copy.path)}, ${copy.modules.length} module(s)`;
  59. if (importer !== undefined) {
  60. message += `\n Included by ${importer.readableIdentifier(requestShortener)}`;
  61. }
  62. return message;
  63. })
  64. .join("\n");
  65. super(`webpack performance recommendations:
  66. ${headline}
  67. ${copiesList}
  68. Every copy is bundled separately and has its own module state, which increases the output size and can break \`instanceof\` checks and configuration shared through the package.
  69. Align the version ranges requiring this package, or force a single version with "resolutions"/"overrides" in your package.json.`);
  70. /** @type {string} */
  71. this.name = "DuplicatePackagesWarning";
  72. /** @type {Module} */
  73. this.module = copies[0].modules[0];
  74. }
  75. }
  76. module.exports = DuplicatePackagesWarning;