ImportScriptsChunkLoadingRuntimeModule.js 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  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 { ReadOnlyRuntimeRequirements } from "../Module" */
  19. class ImportScriptsChunkLoadingRuntimeModule extends RuntimeModule {
  20. /**
  21. * @param {ReadOnlyRuntimeRequirements} runtimeRequirements runtime requirements
  22. * @param {boolean} withCreateScriptUrl with createScriptUrl support
  23. */
  24. constructor(runtimeRequirements, withCreateScriptUrl) {
  25. super("importScripts chunk loading", RuntimeModule.STAGE_ATTACH);
  26. /** @type {ReadOnlyRuntimeRequirements} */
  27. this.runtimeRequirements = runtimeRequirements;
  28. /** @type {boolean} */
  29. this._withCreateScriptUrl = withCreateScriptUrl;
  30. }
  31. /**
  32. * @private
  33. * @param {Chunk} chunk chunk
  34. * @returns {string} generated code
  35. */
  36. _generateBaseUri(chunk) {
  37. const options = chunk.getEntryOptions();
  38. const compilation = /** @type {Compilation} */ (this.compilation);
  39. const rootOutputDir = compilation.runtimeTemplate.chunkRootOutputDir(
  40. chunk,
  41. false
  42. );
  43. return renderBaseUri(
  44. options ? options.baseUri : undefined,
  45. `self.location + ${JSON.stringify(rootOutputDir ? `/../${rootOutputDir}` : "")}`
  46. );
  47. }
  48. /**
  49. * Generates runtime code for this runtime module.
  50. * @returns {string | null} runtime code
  51. */
  52. generate() {
  53. const compilation = /** @type {Compilation} */ (this.compilation);
  54. const fn = RuntimeGlobals.ensureChunkHandlers;
  55. const withBaseURI = this.runtimeRequirements.has(RuntimeGlobals.baseURI);
  56. const withLoading = this.runtimeRequirements.has(
  57. RuntimeGlobals.ensureChunkHandlers
  58. );
  59. const withCallback = this.runtimeRequirements.has(
  60. RuntimeGlobals.chunkCallback
  61. );
  62. const withHmr = this.runtimeRequirements.has(
  63. RuntimeGlobals.hmrDownloadUpdateHandlers
  64. );
  65. const withHmrManifest = this.runtimeRequirements.has(
  66. RuntimeGlobals.hmrDownloadManifest
  67. );
  68. const globalObject = compilation.runtimeTemplate.globalObject;
  69. const chunkLoadingGlobalExpr = `${globalObject}[${JSON.stringify(
  70. compilation.outputOptions.chunkLoadingGlobal
  71. )}]`;
  72. const chunkGraph = /** @type {ChunkGraph} */ (this.chunkGraph);
  73. const chunk = /** @type {Chunk} */ (this.chunk);
  74. const hasJsMatcher = compileBooleanMatcher(
  75. chunkGraph.getChunkConditionMap(chunk, chunkHasJs)
  76. );
  77. const initialChunkIds = getInitialChunkIds(chunk, chunkGraph, chunkHasJs);
  78. const stateExpression = withHmr
  79. ? `${RuntimeGlobals.hmrRuntimeStatePrefix}_importScripts`
  80. : undefined;
  81. const runtimeTemplate = compilation.runtimeTemplate;
  82. const { _withCreateScriptUrl: withCreateScriptUrl } = this;
  83. const installedChunksObject = `{\n${Template.indent(
  84. Array.from(initialChunkIds, (id) => `${JSON.stringify(id)}: 1`).join(
  85. ",\n"
  86. )
  87. )}\n}`;
  88. // Only the install/loading pair below reads the table. A chunk asking only for
  89. // `.b` gets this module for the base uri alone, and then has nothing to look up.
  90. const withInstalledChunks = withCallback || withLoading || withHmr;
  91. return Template.asString([
  92. withBaseURI ? this._generateBaseUri(chunk) : "// no baseURI",
  93. "",
  94. withInstalledChunks
  95. ? Template.asString([
  96. "// object to store loaded chunks",
  97. '// "1" means "already loaded"',
  98. `var installedChunks = ${
  99. stateExpression
  100. ? runtimeTemplate.assignOr(
  101. stateExpression,
  102. installedChunksObject
  103. )
  104. : installedChunksObject
  105. };`
  106. ])
  107. : "// no installed chunks",
  108. "",
  109. withCallback || withLoading
  110. ? Template.asString([
  111. "// importScripts chunk loading",
  112. `var installChunk = ${runtimeTemplate.basicFunction("data", [
  113. runtimeTemplate.destructureArray(
  114. ["chunkIds", "moreModules", "runtime"],
  115. "data"
  116. ),
  117. "for(var moduleId in moreModules) {",
  118. Template.indent([
  119. `if(${RuntimeGlobals.hasOwnProperty}(moreModules, moduleId)) {`,
  120. Template.indent(
  121. `${RuntimeGlobals.moduleFactories}[moduleId] = moreModules[moduleId];`
  122. ),
  123. "}"
  124. ]),
  125. "}",
  126. `if(runtime) runtime(${RuntimeGlobals.require});`,
  127. "while(chunkIds.length)",
  128. Template.indent("installedChunks[chunkIds.pop()] = 1;"),
  129. "parentChunkLoadingFunction(data);"
  130. ])};`
  131. ])
  132. : "// no chunk install function needed",
  133. withCallback || withLoading
  134. ? Template.asString([
  135. withLoading
  136. ? `${fn}.i = ${runtimeTemplate.basicFunction(
  137. "chunkId, promises",
  138. hasJsMatcher !== false
  139. ? [
  140. '// "1" is the signal for "already loaded"',
  141. "if(!installedChunks[chunkId]) {",
  142. Template.indent([
  143. hasJsMatcher === true
  144. ? "if(true) { // all chunks have JS"
  145. : `if(${hasJsMatcher("chunkId")}) {`,
  146. Template.indent(
  147. `importScripts(${
  148. withCreateScriptUrl
  149. ? `${RuntimeGlobals.createScriptUrl}(${RuntimeGlobals.publicPath} + ${RuntimeGlobals.getChunkScriptFilename}(chunkId))`
  150. : `${RuntimeGlobals.publicPath} + ${RuntimeGlobals.getChunkScriptFilename}(chunkId)`
  151. });`
  152. ),
  153. "}"
  154. ]),
  155. "}"
  156. ]
  157. : "installedChunks[chunkId] = 1;"
  158. )};`
  159. : "",
  160. "",
  161. `var chunkLoadingGlobal = ${runtimeTemplate.assignOr(chunkLoadingGlobalExpr, "[]")};`,
  162. "var parentChunkLoadingFunction = chunkLoadingGlobal.push.bind(chunkLoadingGlobal);",
  163. // Install chunks pushed before this runtime loaded (worklets
  164. // pre-load every chunk via `addModule` before the entry runs).
  165. "chunkLoadingGlobal.forEach(installChunk);",
  166. "chunkLoadingGlobal.push = installChunk;"
  167. ])
  168. : "// no chunk loading",
  169. "",
  170. withHmr
  171. ? Template.asString([
  172. "function loadUpdateChunk(chunkId, updatedModulesList) {",
  173. Template.indent([
  174. "var success = false;",
  175. `${globalObject}[${JSON.stringify(
  176. compilation.outputOptions.hotUpdateGlobal
  177. )}] = ${runtimeTemplate.basicFunction("_, moreModules, runtime", [
  178. "for(var moduleId in moreModules) {",
  179. Template.indent([
  180. `if(${RuntimeGlobals.hasOwnProperty}(moreModules, moduleId)) {`,
  181. Template.indent([
  182. "currentUpdate[moduleId] = moreModules[moduleId];",
  183. `${runtimeTemplate.optionalChaining("updatedModulesList", "push(moduleId)")};`
  184. ]),
  185. "}"
  186. ]),
  187. "}",
  188. "if(runtime) currentUpdateRuntime.push(runtime);",
  189. "success = true;"
  190. ])};`,
  191. "// start update chunk loading",
  192. `importScripts(${
  193. withCreateScriptUrl
  194. ? `${RuntimeGlobals.createScriptUrl}(${RuntimeGlobals.publicPath} + ${RuntimeGlobals.getChunkUpdateScriptFilename}(chunkId))`
  195. : `${RuntimeGlobals.publicPath} + ${RuntimeGlobals.getChunkUpdateScriptFilename}(chunkId)`
  196. });`,
  197. 'if(!success) throw new Error("Loading update chunk failed for unknown reason");'
  198. ]),
  199. "}",
  200. "",
  201. generateJavascriptHMR("importScripts")
  202. ])
  203. : "// no HMR",
  204. "",
  205. withHmrManifest
  206. ? Template.asString([
  207. `${
  208. RuntimeGlobals.hmrDownloadManifest
  209. } = ${runtimeTemplate.basicFunction("", [
  210. 'if (typeof fetch === "undefined") throw new Error("No browser support: need fetch API");',
  211. `return fetch(${RuntimeGlobals.publicPath} + ${
  212. RuntimeGlobals.getUpdateManifestFilename
  213. }()).then(${runtimeTemplate.basicFunction("response", [
  214. "if(response.status === 404) return; // no update available",
  215. 'if(!response.ok) throw new Error("Failed to fetch update manifest " + response.statusText);',
  216. "return response.json();"
  217. ])});`
  218. ])};`
  219. ])
  220. : "// no HMR manifest"
  221. ]);
  222. }
  223. }
  224. module.exports = ImportScriptsChunkLoadingRuntimeModule;