StartupHelpers.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  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 Template = require("../Template");
  8. const { isSubset } = require("../util/SetHelpers");
  9. const { getAllChunks } = require("./ChunkHelpers");
  10. /** @import Hash from "../util/Hash" */
  11. /** @import Chunk, { ChunkId } from "../Chunk" */
  12. /**
  13. * @import ChunkGraph, {
  14. * ModuleId,
  15. * EntryModuleWithChunkGroup
  16. * } from "../ChunkGraph"
  17. */
  18. /** @import Entrypoint from "../Entrypoint" */
  19. /** @import RuntimeTemplate from "../RuntimeTemplate" */
  20. const EXPORT_PREFIX = `var ${RuntimeGlobals.exports} = `;
  21. /** @typedef {Set<Chunk>} Chunks */
  22. /** @typedef {ModuleId[]} ModuleIds */
  23. /**
  24. * The module id an entry helper runs, assigned to `entryModuleId` only where a reader
  25. * (`require.main`, `import.meta.main`) asked for that global itself.
  26. * @param {ChunkGraph} chunkGraph chunkGraph
  27. * @param {Chunk} chunk the chunk the helper is rendered into
  28. * @param {string} moduleIdExpression the module id the helper runs
  29. * @returns {string} what to hand `__webpack_require__`
  30. */
  31. const entryModuleIdExpression = (chunkGraph, chunk, moduleIdExpression) =>
  32. chunkGraph.getTreeRuntimeRequirements(chunk).has(RuntimeGlobals.entryModuleId)
  33. ? `${RuntimeGlobals.entryModuleId} = ${moduleIdExpression}`
  34. : moduleIdExpression;
  35. module.exports.entryModuleIdExpression = entryModuleIdExpression;
  36. /**
  37. * Whether the startup rendered for this chunk waits on other chunks — the one shape
  38. * `generateEntryStartup` gives a startup helper, rather than calling the entry module
  39. * straight. Asked while runtime requirements are still open, where that render is long
  40. * off, so it mirrors its loop rather than reading the output.
  41. * @param {ChunkGraph} chunkGraph chunkGraph
  42. * @param {Chunk} chunk chunk
  43. * @returns {boolean} true when an entry module of this chunk runs behind a chunk load
  44. */
  45. module.exports.entryStartupAwaitsChunks = (chunkGraph, chunk) => {
  46. for (const [
  47. module,
  48. entrypoint
  49. ] of chunkGraph.getChunkEntryModulesWithChunkGroupIterable(chunk)) {
  50. if (!chunkGraph.getModuleSourceTypes(module).has("javascript")) {
  51. continue;
  52. }
  53. const group = /** @type {Entrypoint} */ (entrypoint);
  54. if (getAllChunks(group, chunk, group.getRuntimeChunk()).size > 0) {
  55. return true;
  56. }
  57. }
  58. return false;
  59. };
  60. /**
  61. * Returns runtime code.
  62. * @param {ChunkGraph} chunkGraph chunkGraph
  63. * @param {RuntimeTemplate} runtimeTemplate runtimeTemplate
  64. * @param {EntryModuleWithChunkGroup[]} entries entries
  65. * @param {Chunk} chunk chunk
  66. * @param {boolean} passive true: passive startup with on chunks loaded
  67. * @returns {string} runtime code
  68. */
  69. module.exports.generateEntryStartup = (
  70. chunkGraph,
  71. runtimeTemplate,
  72. entries,
  73. chunk,
  74. passive
  75. ) => {
  76. /** @type {string[]} */
  77. const runtime = [
  78. `var __webpack_exec__ = ${runtimeTemplate.returningFunction(
  79. `${RuntimeGlobals.require}(${entryModuleIdExpression(
  80. chunkGraph,
  81. chunk,
  82. "moduleId"
  83. )})`,
  84. "moduleId"
  85. )}`
  86. ];
  87. /**
  88. * Returns fn to execute.
  89. * @param {ModuleId} id id
  90. * @returns {string} fn to execute
  91. */
  92. const runModule = (id) => `__webpack_exec__(${JSON.stringify(id)})`;
  93. /**
  94. * Output combination.
  95. * @param {Chunks} chunks chunks
  96. * @param {ModuleIds} moduleIds module ids
  97. * @param {boolean=} final true when final, otherwise false
  98. */
  99. const outputCombination = (chunks, moduleIds, final) => {
  100. if (chunks.size === 0) {
  101. runtime.push(
  102. `${final ? EXPORT_PREFIX : ""}(${moduleIds.map(runModule).join(", ")});`
  103. );
  104. } else {
  105. const fn = runtimeTemplate.returningFunction(
  106. moduleIds.map(runModule).join(", ")
  107. );
  108. runtime.push(
  109. `${final && !passive ? EXPORT_PREFIX : ""}${
  110. passive
  111. ? RuntimeGlobals.onChunksLoaded
  112. : RuntimeGlobals.startupEntrypoint
  113. }(0, ${JSON.stringify(Array.from(chunks, (c) => c.id))}, ${fn});`
  114. );
  115. if (final && passive) {
  116. runtime.push(`${EXPORT_PREFIX}${RuntimeGlobals.onChunksLoaded}();`);
  117. }
  118. }
  119. };
  120. /** @type {Chunks | undefined} */
  121. let currentChunks;
  122. /** @type {ModuleIds | undefined} */
  123. let currentModuleIds;
  124. for (const [module, entrypoint] of entries) {
  125. if (!chunkGraph.getModuleSourceTypes(module).has("javascript")) {
  126. continue;
  127. }
  128. const runtimeChunk =
  129. /** @type {Entrypoint} */
  130. (entrypoint).getRuntimeChunk();
  131. const moduleId = /** @type {ModuleId} */ (chunkGraph.getModuleId(module));
  132. const chunks = getAllChunks(
  133. /** @type {Entrypoint} */
  134. (entrypoint),
  135. chunk,
  136. runtimeChunk
  137. );
  138. if (
  139. currentChunks &&
  140. currentChunks.size === chunks.size &&
  141. isSubset(currentChunks, chunks)
  142. ) {
  143. /** @type {ModuleIds} */
  144. (currentModuleIds).push(moduleId);
  145. } else {
  146. if (currentChunks) {
  147. outputCombination(
  148. currentChunks,
  149. /** @type {ModuleIds} */ (currentModuleIds)
  150. );
  151. }
  152. currentChunks = chunks;
  153. currentModuleIds = [moduleId];
  154. }
  155. }
  156. // output current modules with export prefix
  157. if (currentChunks) {
  158. outputCombination(
  159. currentChunks,
  160. /** @type {ModuleIds} */
  161. (currentModuleIds),
  162. true
  163. );
  164. }
  165. runtime.push("");
  166. return Template.asString(runtime);
  167. };
  168. /**
  169. * Returns initially fulfilled chunk ids.
  170. * @param {Chunk} chunk the chunk
  171. * @param {ChunkGraph} chunkGraph the chunk graph
  172. * @param {(chunk: Chunk, chunkGraph: ChunkGraph) => boolean} filterFn filter function
  173. * @returns {Set<ChunkId>} initially fulfilled chunk ids
  174. */
  175. module.exports.getInitialChunkIds = (chunk, chunkGraph, filterFn) => {
  176. /** @type {Set<ChunkId>} */
  177. const initialChunkIds = new Set(chunk.ids);
  178. for (const c of chunk.getAllInitialChunks()) {
  179. if (c === chunk || filterFn(c, chunkGraph)) continue;
  180. for (const id of /** @type {ChunkId[]} */ (c.ids)) {
  181. initialChunkIds.add(id);
  182. }
  183. }
  184. return initialChunkIds;
  185. };
  186. /**
  187. * Processes the provided hash.
  188. * @param {Hash} hash the hash to update
  189. * @param {ChunkGraph} chunkGraph chunkGraph
  190. * @param {EntryModuleWithChunkGroup[]} entries entries
  191. * @param {Chunk} chunk chunk
  192. * @returns {void}
  193. */
  194. module.exports.updateHashForEntryStartup = (
  195. hash,
  196. chunkGraph,
  197. entries,
  198. chunk
  199. ) => {
  200. for (const [module, entrypoint] of entries) {
  201. const runtimeChunk =
  202. /** @type {Entrypoint} */
  203. (entrypoint).getRuntimeChunk();
  204. const moduleId = chunkGraph.getModuleId(module);
  205. hash.update(`${moduleId}`);
  206. for (const c of getAllChunks(
  207. /** @type {Entrypoint} */ (entrypoint),
  208. chunk,
  209. /** @type {Chunk} */ (runtimeChunk)
  210. )) {
  211. hash.update(`${c.id}`);
  212. }
  213. }
  214. };