ModuleInfoHeaderPlugin.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const { ConcatSource, RawSource, CachedSource } = require("webpack-sources");
  7. const { UsageState } = require("./ExportsInfo");
  8. const Template = require("./Template");
  9. const JavascriptModulesPlugin = require("./javascript/JavascriptModulesPlugin");
  10. /** @typedef {import("webpack-sources").Source} Source */
  11. /** @typedef {import("./Compiler")} Compiler */
  12. /** @typedef {import("./ExportsInfo")} ExportsInfo */
  13. /** @typedef {import("./ExportsInfo").ExportInfo} ExportInfo */
  14. /** @typedef {import("./Module")} Module */
  15. /** @typedef {import("./ModuleGraph")} ModuleGraph */
  16. /** @typedef {import("./ModuleTemplate")} ModuleTemplate */
  17. /** @typedef {import("./RequestShortener")} RequestShortener */
  18. const joinIterableWithComma = iterable => {
  19. // This is more performant than Array.from().join(", ")
  20. // as it doesn't create an array
  21. let str = "";
  22. let first = true;
  23. for (const item of iterable) {
  24. if (first) {
  25. first = false;
  26. } else {
  27. str += ", ";
  28. }
  29. str += item;
  30. }
  31. return str;
  32. };
  33. /**
  34. * @param {ConcatSource} source output
  35. * @param {string} indent spacing
  36. * @param {ExportsInfo} exportsInfo data
  37. * @param {ModuleGraph} moduleGraph moduleGraph
  38. * @param {RequestShortener} requestShortener requestShortener
  39. * @param {Set<ExportInfo>} alreadyPrinted deduplication set
  40. * @returns {void}
  41. */
  42. const printExportsInfoToSource = (
  43. source,
  44. indent,
  45. exportsInfo,
  46. moduleGraph,
  47. requestShortener,
  48. alreadyPrinted = new Set()
  49. ) => {
  50. const otherExportsInfo = exportsInfo.otherExportsInfo;
  51. let alreadyPrintedExports = 0;
  52. // determine exports to print
  53. const printedExports = [];
  54. for (const exportInfo of exportsInfo.orderedExports) {
  55. if (!alreadyPrinted.has(exportInfo)) {
  56. alreadyPrinted.add(exportInfo);
  57. printedExports.push(exportInfo);
  58. } else {
  59. alreadyPrintedExports++;
  60. }
  61. }
  62. let showOtherExports = false;
  63. if (!alreadyPrinted.has(otherExportsInfo)) {
  64. alreadyPrinted.add(otherExportsInfo);
  65. showOtherExports = true;
  66. } else {
  67. alreadyPrintedExports++;
  68. }
  69. // print the exports
  70. for (const exportInfo of printedExports) {
  71. const target = exportInfo.getTarget(moduleGraph);
  72. source.add(
  73. Template.toComment(
  74. `${indent}export ${JSON.stringify(exportInfo.name).slice(
  75. 1,
  76. -1
  77. )} [${exportInfo.getProvidedInfo()}] [${exportInfo.getUsedInfo()}] [${exportInfo.getRenameInfo()}]${
  78. target
  79. ? ` -> ${target.module.readableIdentifier(requestShortener)}${
  80. target.export
  81. ? ` .${target.export
  82. .map(e => JSON.stringify(e).slice(1, -1))
  83. .join(".")}`
  84. : ""
  85. }`
  86. : ""
  87. }`
  88. ) + "\n"
  89. );
  90. if (exportInfo.exportsInfo) {
  91. printExportsInfoToSource(
  92. source,
  93. indent + " ",
  94. exportInfo.exportsInfo,
  95. moduleGraph,
  96. requestShortener,
  97. alreadyPrinted
  98. );
  99. }
  100. }
  101. if (alreadyPrintedExports) {
  102. source.add(
  103. Template.toComment(
  104. `${indent}... (${alreadyPrintedExports} already listed exports)`
  105. ) + "\n"
  106. );
  107. }
  108. if (showOtherExports) {
  109. const target = otherExportsInfo.getTarget(moduleGraph);
  110. if (
  111. target ||
  112. otherExportsInfo.provided !== false ||
  113. otherExportsInfo.getUsed(undefined) !== UsageState.Unused
  114. ) {
  115. const title =
  116. printedExports.length > 0 || alreadyPrintedExports > 0
  117. ? "other exports"
  118. : "exports";
  119. source.add(
  120. Template.toComment(
  121. `${indent}${title} [${otherExportsInfo.getProvidedInfo()}] [${otherExportsInfo.getUsedInfo()}]${
  122. target
  123. ? ` -> ${target.module.readableIdentifier(requestShortener)}`
  124. : ""
  125. }`
  126. ) + "\n"
  127. );
  128. }
  129. }
  130. };
  131. /** @type {WeakMap<RequestShortener, WeakMap<Module, { header: RawSource, full: WeakMap<Source, CachedSource> }>>} */
  132. const caches = new WeakMap();
  133. class ModuleInfoHeaderPlugin {
  134. /**
  135. * @param {boolean=} verbose add more information like exports, runtime requirements and bailouts
  136. */
  137. constructor(verbose = true) {
  138. this._verbose = verbose;
  139. }
  140. /**
  141. * @param {Compiler} compiler the compiler
  142. * @returns {void}
  143. */
  144. apply(compiler) {
  145. const { _verbose: verbose } = this;
  146. compiler.hooks.compilation.tap("ModuleInfoHeaderPlugin", compilation => {
  147. const hooks = JavascriptModulesPlugin.getCompilationHooks(compilation);
  148. hooks.renderModulePackage.tap(
  149. "ModuleInfoHeaderPlugin",
  150. (
  151. moduleSource,
  152. module,
  153. { chunk, chunkGraph, moduleGraph, runtimeTemplate }
  154. ) => {
  155. const { requestShortener } = runtimeTemplate;
  156. let cacheEntry;
  157. let cache = caches.get(requestShortener);
  158. if (cache === undefined) {
  159. caches.set(requestShortener, (cache = new WeakMap()));
  160. cache.set(
  161. module,
  162. (cacheEntry = { header: undefined, full: new WeakMap() })
  163. );
  164. } else {
  165. cacheEntry = cache.get(module);
  166. if (cacheEntry === undefined) {
  167. cache.set(
  168. module,
  169. (cacheEntry = { header: undefined, full: new WeakMap() })
  170. );
  171. } else if (!verbose) {
  172. const cachedSource = cacheEntry.full.get(moduleSource);
  173. if (cachedSource !== undefined) return cachedSource;
  174. }
  175. }
  176. const source = new ConcatSource();
  177. let header = cacheEntry.header;
  178. if (header === undefined) {
  179. const req = module.readableIdentifier(requestShortener);
  180. const reqStr = req.replace(/\*\//g, "*_/");
  181. const reqStrStar = "*".repeat(reqStr.length);
  182. const headerStr = `/*!****${reqStrStar}****!*\\\n !*** ${reqStr} ***!\n \\****${reqStrStar}****/\n`;
  183. header = new RawSource(headerStr);
  184. cacheEntry.header = header;
  185. }
  186. source.add(header);
  187. if (verbose) {
  188. const exportsType = module.buildMeta.exportsType;
  189. source.add(
  190. Template.toComment(
  191. exportsType
  192. ? `${exportsType} exports`
  193. : "unknown exports (runtime-defined)"
  194. ) + "\n"
  195. );
  196. if (exportsType) {
  197. const exportsInfo = moduleGraph.getExportsInfo(module);
  198. printExportsInfoToSource(
  199. source,
  200. "",
  201. exportsInfo,
  202. moduleGraph,
  203. requestShortener
  204. );
  205. }
  206. source.add(
  207. Template.toComment(
  208. `runtime requirements: ${joinIterableWithComma(
  209. chunkGraph.getModuleRuntimeRequirements(module, chunk.runtime)
  210. )}`
  211. ) + "\n"
  212. );
  213. const optimizationBailout =
  214. moduleGraph.getOptimizationBailout(module);
  215. if (optimizationBailout) {
  216. for (const text of optimizationBailout) {
  217. let code;
  218. if (typeof text === "function") {
  219. code = text(requestShortener);
  220. } else {
  221. code = text;
  222. }
  223. source.add(Template.toComment(`${code}`) + "\n");
  224. }
  225. }
  226. source.add(moduleSource);
  227. return source;
  228. } else {
  229. source.add(moduleSource);
  230. const cachedSource = new CachedSource(source);
  231. cacheEntry.full.set(moduleSource, cachedSource);
  232. return cachedSource;
  233. }
  234. }
  235. );
  236. hooks.chunkHash.tap("ModuleInfoHeaderPlugin", (chunk, hash) => {
  237. hash.update("ModuleInfoHeaderPlugin");
  238. hash.update("1");
  239. });
  240. });
  241. }
  242. }
  243. module.exports = ModuleInfoHeaderPlugin;