SizeLimitsPlugin.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Sean Larkin @thelarkinn
  4. */
  5. "use strict";
  6. const { find } = require("../util/SetHelpers");
  7. const AssetsOverSizeLimitWarning = require("./AssetsOverSizeLimitWarning");
  8. const EntrypointsOverSizeLimitWarning = require("./EntrypointsOverSizeLimitWarning");
  9. const NoAsyncChunksWarning = require("./NoAsyncChunksWarning");
  10. /** @typedef {import("webpack-sources").Source} Source */
  11. /** @typedef {import("../../declarations/WebpackOptions").PerformanceOptions} PerformanceOptions */
  12. /** @typedef {import("../ChunkGroup")} ChunkGroup */
  13. /** @typedef {import("../Compiler")} Compiler */
  14. /** @typedef {import("../Entrypoint")} Entrypoint */
  15. /** @typedef {import("../WebpackError")} WebpackError */
  16. /**
  17. * @typedef {Object} AssetDetails
  18. * @property {string} name
  19. * @property {number} size
  20. */
  21. /**
  22. * @typedef {Object} EntrypointDetails
  23. * @property {string} name
  24. * @property {number} size
  25. * @property {string[]} files
  26. */
  27. const isOverSizeLimitSet = new WeakSet();
  28. const excludeSourceMap = (name, source, info) => !info.development;
  29. module.exports = class SizeLimitsPlugin {
  30. /**
  31. * @param {PerformanceOptions} options the plugin options
  32. */
  33. constructor(options) {
  34. this.hints = options.hints;
  35. this.maxAssetSize = options.maxAssetSize;
  36. this.maxEntrypointSize = options.maxEntrypointSize;
  37. this.assetFilter = options.assetFilter;
  38. }
  39. /**
  40. * @param {ChunkGroup | Source} thing the resource to test
  41. * @returns {boolean} true if over the limit
  42. */
  43. static isOverSizeLimit(thing) {
  44. return isOverSizeLimitSet.has(thing);
  45. }
  46. /**
  47. * Apply the plugin
  48. * @param {Compiler} compiler the compiler instance
  49. * @returns {void}
  50. */
  51. apply(compiler) {
  52. const entrypointSizeLimit = this.maxEntrypointSize;
  53. const assetSizeLimit = this.maxAssetSize;
  54. const hints = this.hints;
  55. const assetFilter = this.assetFilter || excludeSourceMap;
  56. compiler.hooks.afterEmit.tap("SizeLimitsPlugin", compilation => {
  57. /** @type {WebpackError[]} */
  58. const warnings = [];
  59. /**
  60. * @param {Entrypoint} entrypoint an entrypoint
  61. * @returns {number} the size of the entrypoint
  62. */
  63. const getEntrypointSize = entrypoint => {
  64. let size = 0;
  65. for (const file of entrypoint.getFiles()) {
  66. const asset = compilation.getAsset(file);
  67. if (
  68. asset &&
  69. assetFilter(asset.name, asset.source, asset.info) &&
  70. asset.source
  71. ) {
  72. size += asset.info.size || asset.source.size();
  73. }
  74. }
  75. return size;
  76. };
  77. /** @type {AssetDetails[]} */
  78. const assetsOverSizeLimit = [];
  79. for (const { name, source, info } of compilation.getAssets()) {
  80. if (!assetFilter(name, source, info) || !source) {
  81. continue;
  82. }
  83. const size = info.size || source.size();
  84. if (size > /** @type {number} */ (assetSizeLimit)) {
  85. assetsOverSizeLimit.push({
  86. name,
  87. size
  88. });
  89. isOverSizeLimitSet.add(source);
  90. }
  91. }
  92. const fileFilter = name => {
  93. const asset = compilation.getAsset(name);
  94. return asset && assetFilter(asset.name, asset.source, asset.info);
  95. };
  96. /** @type {EntrypointDetails[]} */
  97. const entrypointsOverLimit = [];
  98. for (const [name, entry] of compilation.entrypoints) {
  99. const size = getEntrypointSize(entry);
  100. if (size > /** @type {number} */ (entrypointSizeLimit)) {
  101. entrypointsOverLimit.push({
  102. name: name,
  103. size: size,
  104. files: entry.getFiles().filter(fileFilter)
  105. });
  106. isOverSizeLimitSet.add(entry);
  107. }
  108. }
  109. if (hints) {
  110. // 1. Individual Chunk: Size < 250kb
  111. // 2. Collective Initial Chunks [entrypoint] (Each Set?): Size < 250kb
  112. // 3. No Async Chunks
  113. // if !1, then 2, if !2 return
  114. if (assetsOverSizeLimit.length > 0) {
  115. warnings.push(
  116. new AssetsOverSizeLimitWarning(
  117. assetsOverSizeLimit,
  118. /** @type {number} */ (assetSizeLimit)
  119. )
  120. );
  121. }
  122. if (entrypointsOverLimit.length > 0) {
  123. warnings.push(
  124. new EntrypointsOverSizeLimitWarning(
  125. entrypointsOverLimit,
  126. /** @type {number} */ (entrypointSizeLimit)
  127. )
  128. );
  129. }
  130. if (warnings.length > 0) {
  131. const someAsyncChunk = find(
  132. compilation.chunks,
  133. chunk => !chunk.canBeInitial()
  134. );
  135. if (!someAsyncChunk) {
  136. warnings.push(new NoAsyncChunksWarning());
  137. }
  138. if (hints === "error") {
  139. compilation.errors.push(...warnings);
  140. } else {
  141. compilation.warnings.push(...warnings);
  142. }
  143. }
  144. }
  145. });
  146. }
  147. };