RequireChunkLoadingRuntimeModule.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. */
  4. "use strict";
  5. const RuntimeGlobals = require("../RuntimeGlobals");
  6. const RuntimeModule = require("../RuntimeModule");
  7. const Template = require("../Template");
  8. const {
  9. generateJavascriptHMR
  10. } = require("../hmr/JavascriptHotModuleReplacementHelper");
  11. const { chunkHasJs } = require("../javascript/JavascriptModulesPlugin");
  12. const { getInitialChunkIds } = require("../javascript/StartupHelpers");
  13. const { renderBaseUri } = require("../runtime/baseUri");
  14. const compileBooleanMatcher = require("../util/compileBooleanMatcher");
  15. /** @import Chunk from "../Chunk" */
  16. /** @import ChunkGraph from "../ChunkGraph" */
  17. /** @import Compilation from "../Compilation" */
  18. /** @import RuntimeTemplate from "../RuntimeTemplate" */
  19. /** @import { ReadOnlyRuntimeRequirements } from "../Module" */
  20. class RequireChunkLoadingRuntimeModule extends RuntimeModule {
  21. /**
  22. * Creates an instance of RequireChunkLoadingRuntimeModule.
  23. * @param {ReadOnlyRuntimeRequirements} runtimeRequirements runtime requirements
  24. */
  25. constructor(runtimeRequirements) {
  26. super("require chunk loading", RuntimeModule.STAGE_ATTACH);
  27. /** @type {ReadOnlyRuntimeRequirements} */
  28. this.runtimeRequirements = runtimeRequirements;
  29. }
  30. /**
  31. * Returns generated code.
  32. * @private
  33. * @param {Chunk} chunk chunk
  34. * @param {string} rootOutputDir root output directory
  35. * @param {RuntimeTemplate} runtimeTemplate the runtime template
  36. * @returns {string} generated code
  37. */
  38. _generateBaseUri(chunk, rootOutputDir, runtimeTemplate) {
  39. const options = chunk.getEntryOptions();
  40. return renderBaseUri(
  41. options ? options.baseUri : undefined,
  42. `require(${runtimeTemplate.renderNodePrefixForCoreModule("url")}).pathToFileURL(${
  43. rootOutputDir !== "./"
  44. ? `__dirname + ${JSON.stringify(`/${rootOutputDir}`)}`
  45. : "__filename"
  46. })`
  47. );
  48. }
  49. /**
  50. * Generates runtime code for this runtime module.
  51. * @returns {string | null} runtime code
  52. */
  53. generate() {
  54. const compilation = /** @type {Compilation} */ (this.compilation);
  55. const chunkGraph = /** @type {ChunkGraph} */ (this.chunkGraph);
  56. const chunk = /** @type {Chunk} */ (this.chunk);
  57. const { runtimeTemplate } = compilation;
  58. const fn = RuntimeGlobals.ensureChunkHandlers;
  59. const withBaseURI = this.runtimeRequirements.has(RuntimeGlobals.baseURI);
  60. const withExternalInstallChunk = this.runtimeRequirements.has(
  61. RuntimeGlobals.externalInstallChunk
  62. );
  63. const withOnChunkLoad = this.runtimeRequirements.has(
  64. RuntimeGlobals.onChunksLoaded
  65. );
  66. const withLoading = this.runtimeRequirements.has(
  67. RuntimeGlobals.ensureChunkHandlers
  68. );
  69. const withHmr = this.runtimeRequirements.has(
  70. RuntimeGlobals.hmrDownloadUpdateHandlers
  71. );
  72. const withHmrManifest = this.runtimeRequirements.has(
  73. RuntimeGlobals.hmrDownloadManifest
  74. );
  75. const conditionMap = chunkGraph.getChunkConditionMap(chunk, chunkHasJs);
  76. const hasJsMatcher = compileBooleanMatcher(conditionMap);
  77. const initialChunkIds = getInitialChunkIds(chunk, chunkGraph, chunkHasJs);
  78. const rootOutputDir = runtimeTemplate.chunkRootOutputDir(chunk, true);
  79. const stateExpression = withHmr
  80. ? `${RuntimeGlobals.hmrRuntimeStatePrefix}_require`
  81. : undefined;
  82. const cst = runtimeTemplate.renderConst();
  83. const installedChunksObject = `{\n${Template.indent(
  84. Array.from(initialChunkIds, (id) => `${JSON.stringify(id)}: 1`).join(
  85. ",\n"
  86. )
  87. )}\n}`;
  88. // Every part below that reads the table. A chunk asking only for `.b` gets this
  89. // module for the base uri alone, and then has nothing to look up.
  90. const withInstalledChunks =
  91. withOnChunkLoad || withLoading || withExternalInstallChunk || withHmr;
  92. return Template.asString([
  93. withBaseURI
  94. ? this._generateBaseUri(chunk, rootOutputDir, runtimeTemplate)
  95. : "// no baseURI",
  96. "",
  97. withInstalledChunks
  98. ? Template.asString([
  99. "// object to store loaded chunks",
  100. '// "1" means "loaded", otherwise not loaded yet',
  101. `${cst} installedChunks = ${
  102. stateExpression
  103. ? runtimeTemplate.assignOr(
  104. stateExpression,
  105. installedChunksObject
  106. )
  107. : installedChunksObject
  108. };`
  109. ])
  110. : "// no installed chunks",
  111. "",
  112. withOnChunkLoad
  113. ? `${
  114. RuntimeGlobals.onChunksLoaded
  115. }.require = ${runtimeTemplate.returningFunction(
  116. "installedChunks[chunkId]",
  117. "chunkId"
  118. )};`
  119. : "// no on chunks loaded",
  120. "",
  121. withLoading || withExternalInstallChunk
  122. ? `${cst} installChunk = ${runtimeTemplate.basicFunction("chunk", [
  123. `${cst} moreModules = chunk.modules, chunkIds = chunk.ids, runtime = chunk.runtime;`,
  124. "for(var moduleId in moreModules) {",
  125. Template.indent([
  126. `if(${RuntimeGlobals.hasOwnProperty}(moreModules, moduleId)) {`,
  127. Template.indent([
  128. `${RuntimeGlobals.moduleFactories}[moduleId] = moreModules[moduleId];`
  129. ]),
  130. "}"
  131. ]),
  132. "}",
  133. `if(runtime) runtime(${RuntimeGlobals.require});`,
  134. "for(var i = 0; i < chunkIds.length; i++)",
  135. Template.indent("installedChunks[chunkIds[i]] = 1;"),
  136. withOnChunkLoad ? `${RuntimeGlobals.onChunksLoaded}();` : ""
  137. ])};`
  138. : "// no chunk install function needed",
  139. "",
  140. withLoading
  141. ? Template.asString([
  142. "// require() chunk loading for javascript",
  143. `${fn}.require = ${runtimeTemplate.basicFunction(
  144. "chunkId, promises",
  145. hasJsMatcher !== false
  146. ? [
  147. '// "1" is the signal for "already loaded"',
  148. "if(!installedChunks[chunkId]) {",
  149. Template.indent([
  150. hasJsMatcher === true
  151. ? "if(true) { // all chunks have JS"
  152. : `if(${hasJsMatcher("chunkId")}) {`,
  153. Template.indent([
  154. // The require function loads and runs a chunk. When the chunk is being run,
  155. // it can call __webpack_require__.C to directly complete installed.
  156. `${cst} installedChunk = require(${JSON.stringify(
  157. rootOutputDir
  158. )} + ${
  159. RuntimeGlobals.getChunkScriptFilename
  160. }(chunkId));`,
  161. "if (!installedChunks[chunkId]) {",
  162. Template.indent(["installChunk(installedChunk);"]),
  163. "}"
  164. ]),
  165. "} else installedChunks[chunkId] = 1;",
  166. ""
  167. ]),
  168. "}"
  169. ]
  170. : "installedChunks[chunkId] = 1;"
  171. )};`
  172. ])
  173. : "// no chunk loading",
  174. "",
  175. withExternalInstallChunk
  176. ? Template.asString([
  177. `module.exports = ${RuntimeGlobals.require};`,
  178. `${RuntimeGlobals.externalInstallChunk} = installChunk;`
  179. ])
  180. : "// no external install chunk",
  181. "",
  182. withHmr
  183. ? Template.asString([
  184. "function loadUpdateChunk(chunkId, updatedModulesList) {",
  185. Template.indent([
  186. `${cst} update = require(${JSON.stringify(rootOutputDir)} + ${
  187. RuntimeGlobals.getChunkUpdateScriptFilename
  188. }(chunkId));`,
  189. `${cst} updatedModules = update.modules;`,
  190. `${cst} runtime = update.runtime;`,
  191. "for(var moduleId in updatedModules) {",
  192. Template.indent([
  193. `if(${RuntimeGlobals.hasOwnProperty}(updatedModules, moduleId)) {`,
  194. Template.indent([
  195. "currentUpdate[moduleId] = updatedModules[moduleId];",
  196. `${runtimeTemplate.optionalChaining("updatedModulesList", "push(moduleId)")};`
  197. ]),
  198. "}"
  199. ]),
  200. "}",
  201. "if(runtime) currentUpdateRuntime.push(runtime);"
  202. ]),
  203. "}",
  204. "",
  205. generateJavascriptHMR("require")
  206. ])
  207. : "// no HMR",
  208. "",
  209. withHmrManifest
  210. ? Template.asString([
  211. `${RuntimeGlobals.hmrDownloadManifest} = function() {`,
  212. Template.indent([
  213. "return Promise.resolve().then(function() {",
  214. Template.indent([
  215. `return require(${JSON.stringify(rootOutputDir)} + ${
  216. RuntimeGlobals.getUpdateManifestFilename
  217. }());`
  218. ]),
  219. `}).catch(${runtimeTemplate.basicFunction("err", [
  220. "if(['MODULE_NOT_FOUND', 'ENOENT'].includes(err.code)) return;",
  221. "throw err;"
  222. ])});`
  223. ]),
  224. "}"
  225. ])
  226. : "// no HMR manifest"
  227. ]);
  228. }
  229. }
  230. module.exports = RequireChunkLoadingRuntimeModule;