ModuleChunkLoadingPlugin.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const RuntimeGlobals = require("../RuntimeGlobals");
  7. const lazyModule = require("../util/lazyModule");
  8. /** @import Chunk from "../Chunk" */
  9. /** @import Compiler from "../Compiler" */
  10. /** @import { RuntimeRequirements } from "../Module" */
  11. // only a chunk that loads other chunks at runtime emits these
  12. const getExportWebpackRequireRuntimeModule = lazyModule(() =>
  13. require("./ExportWebpackRequireRuntimeModule")
  14. );
  15. const getModuleChunkLoadingRuntimeModule = lazyModule(() =>
  16. require("./ModuleChunkLoadingRuntimeModule")
  17. );
  18. const PLUGIN_NAME = "ModuleChunkLoadingPlugin";
  19. class ModuleChunkLoadingPlugin {
  20. /**
  21. * Applies the plugin by registering its hooks on the compiler.
  22. * @param {Compiler} compiler the compiler instance
  23. * @returns {void}
  24. */
  25. apply(compiler) {
  26. compiler.hooks.thisCompilation.tap(PLUGIN_NAME, (compilation) => {
  27. const globalChunkLoading = compilation.outputOptions.chunkLoading;
  28. /**
  29. * Checks whether this module chunk loading plugin is enabled for chunk.
  30. * @param {Chunk} chunk chunk to check
  31. * @returns {boolean} true, when the plugin is enabled for the chunk
  32. */
  33. const isEnabledForChunk = (chunk) => {
  34. const options = chunk.getEntryOptions();
  35. const chunkLoading =
  36. options && options.chunkLoading !== undefined
  37. ? options.chunkLoading
  38. : globalChunkLoading;
  39. return chunkLoading === "import";
  40. };
  41. /** @type {WeakSet<Chunk>} */
  42. const onceForChunkSet = new WeakSet();
  43. /**
  44. * Handles the hook callback for this code path.
  45. * @param {Chunk} chunk chunk to check
  46. * @param {RuntimeRequirements} set runtime requirements
  47. */
  48. const handler = (chunk, set) => {
  49. if (onceForChunkSet.has(chunk)) return;
  50. onceForChunkSet.add(chunk);
  51. if (!isEnabledForChunk(chunk)) return;
  52. compilation.addLazyRuntimeModule(
  53. chunk,
  54. getModuleChunkLoadingRuntimeModule,
  55. (Ctor) => new Ctor(set)
  56. );
  57. };
  58. /**
  59. * Installing a chunk is the only part that writes the module factories and
  60. * looks a chunk id up; a base uri, `onChunksLoaded` or the hot manifest come
  61. * without either. Kept off `handler`, which runs once per chunk and so cannot
  62. * see which of the requirements below the chunk ends up with.
  63. * @param {Chunk} chunk chunk to check
  64. * @param {RuntimeRequirements} set runtime requirements
  65. */
  66. const installHandler = (chunk, set) => {
  67. if (!isEnabledForChunk(chunk)) return;
  68. set.add(RuntimeGlobals.moduleFactoriesAddOnly);
  69. set.add(RuntimeGlobals.hasOwnProperty);
  70. };
  71. for (const requirement of [
  72. RuntimeGlobals.ensureChunkHandlers,
  73. RuntimeGlobals.baseURI,
  74. RuntimeGlobals.externalInstallChunk,
  75. RuntimeGlobals.analyzableChunkImport,
  76. RuntimeGlobals.onChunksLoaded,
  77. RuntimeGlobals.hmrDownloadUpdateHandlers,
  78. RuntimeGlobals.hmrDownloadManifest
  79. ]) {
  80. compilation.hooks.runtimeRequirementInTree
  81. .for(requirement)
  82. .tap(PLUGIN_NAME, handler);
  83. }
  84. for (const requirement of [
  85. RuntimeGlobals.ensureChunkHandlers,
  86. RuntimeGlobals.externalInstallChunk,
  87. RuntimeGlobals.analyzableChunkImport,
  88. RuntimeGlobals.hmrDownloadUpdateHandlers
  89. ]) {
  90. compilation.hooks.runtimeRequirementInTree
  91. .for(requirement)
  92. .tap(PLUGIN_NAME, installHandler);
  93. }
  94. compilation.hooks.runtimeRequirementInTree
  95. .for(RuntimeGlobals.externalInstallChunk)
  96. .tap(PLUGIN_NAME, (chunk) => {
  97. if (!isEnabledForChunk(chunk)) return;
  98. compilation.addLazyRuntimeModule(
  99. chunk,
  100. getExportWebpackRequireRuntimeModule,
  101. (Ctor) => new Ctor()
  102. );
  103. });
  104. // A handler that builds its url asks for both directly: an analyzable
  105. // `import()` may replace `ensureChunkHandlers`, which otherwise pulls them in.
  106. for (const requirement of [
  107. RuntimeGlobals.prefetchChunkHandlers,
  108. RuntimeGlobals.preloadChunkHandlers
  109. ]) {
  110. compilation.hooks.runtimeRequirementInTree
  111. .for(requirement)
  112. .tap(PLUGIN_NAME, (chunk, set, { chunkGraph }) => {
  113. if (!isEnabledForChunk(chunk)) return;
  114. // A baked url carries where the chunk is, so neither is read for it.
  115. // Asked as the runtime module asks, so the two always agree.
  116. if (
  117. compilation.runtimeTemplate.analyzableChunkScriptUrls(
  118. chunk,
  119. chunkGraph,
  120. set
  121. ) !== null
  122. ) {
  123. return;
  124. }
  125. set.add(RuntimeGlobals.publicPath);
  126. set.add(RuntimeGlobals.getChunkScriptFilename);
  127. });
  128. }
  129. // Keyed on `ensureChunk`, not on the handler map: only the runtime chunk loader
  130. // builds a URL from a chunk id, and an analyzable `import()` carries its own.
  131. compilation.hooks.runtimeRequirementInTree
  132. .for(RuntimeGlobals.ensureChunk)
  133. .tap(PLUGIN_NAME, (chunk, set) => {
  134. if (!isEnabledForChunk(chunk)) return;
  135. if (compilation.outputOptions.publicPath !== "auto") {
  136. set.add(RuntimeGlobals.publicPath);
  137. }
  138. set.add(RuntimeGlobals.getChunkScriptFilename);
  139. });
  140. compilation.hooks.runtimeRequirementInTree
  141. .for(RuntimeGlobals.hmrDownloadUpdateHandlers)
  142. .tap(PLUGIN_NAME, (chunk, set) => {
  143. if (!isEnabledForChunk(chunk)) return;
  144. set.add(RuntimeGlobals.publicPath);
  145. set.add(RuntimeGlobals.getChunkUpdateScriptFilename);
  146. // The hot runtime force-loads through the handler map by bare chunk id, so
  147. // the map and loader stay however analyzable — but only where there are
  148. // async chunks to force-load.
  149. if (chunk.hasAsyncChunks()) {
  150. set.add(RuntimeGlobals.ensureChunkHandlers);
  151. set.add(RuntimeGlobals.getChunkScriptFilename);
  152. }
  153. set.add(RuntimeGlobals.moduleCache);
  154. set.add(RuntimeGlobals.hmrModuleData);
  155. set.add(RuntimeGlobals.moduleFactoriesAddOnly);
  156. });
  157. compilation.hooks.runtimeRequirementInTree
  158. .for(RuntimeGlobals.hmrDownloadManifest)
  159. .tap(PLUGIN_NAME, (chunk, set) => {
  160. if (!isEnabledForChunk(chunk)) return;
  161. set.add(RuntimeGlobals.publicPath);
  162. set.add(RuntimeGlobals.getUpdateManifestFilename);
  163. });
  164. compilation.hooks.additionalTreeRuntimeRequirements.tap(
  165. PLUGIN_NAME,
  166. (chunk, set, { chunkGraph }) => {
  167. if (chunkGraph.hasChunkEntryDependentChunks(chunk)) {
  168. set.add(RuntimeGlobals.externalInstallChunk);
  169. }
  170. }
  171. );
  172. });
  173. }
  174. }
  175. module.exports = ModuleChunkLoadingPlugin;