ImportScriptsChunkLoadingRuntimeModule.js 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  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. getChunkFilenameTemplate,
  10. chunkHasJs
  11. } = require("../javascript/JavascriptModulesPlugin");
  12. const { getInitialChunkIds } = require("../javascript/StartupHelpers");
  13. const compileBooleanMatcher = require("../util/compileBooleanMatcher");
  14. const { getUndoPath } = require("../util/identifier");
  15. /** @typedef {import("../Chunk")} Chunk */
  16. /** @typedef {import("../ChunkGraph")} ChunkGraph */
  17. /** @typedef {import("../Compilation")} Compilation */
  18. /** @typedef {import("../Module").ReadOnlyRuntimeRequirements} ReadOnlyRuntimeRequirements */
  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. this.runtimeRequirements = runtimeRequirements;
  27. this._withCreateScriptUrl = withCreateScriptUrl;
  28. }
  29. /**
  30. * @private
  31. * @param {Chunk} chunk chunk
  32. * @returns {string} generated code
  33. */
  34. _generateBaseUri(chunk) {
  35. const options = chunk.getEntryOptions();
  36. if (options && options.baseUri) {
  37. return `${RuntimeGlobals.baseURI} = ${JSON.stringify(options.baseUri)};`;
  38. }
  39. const compilation = /** @type {Compilation} */ (this.compilation);
  40. const outputName = compilation.getPath(
  41. getChunkFilenameTemplate(chunk, compilation.outputOptions),
  42. {
  43. chunk,
  44. contentHashType: "javascript"
  45. }
  46. );
  47. const rootOutputDir = getUndoPath(
  48. outputName,
  49. /** @type {string} */ (compilation.outputOptions.path),
  50. false
  51. );
  52. return `${RuntimeGlobals.baseURI} = self.location + ${JSON.stringify(
  53. rootOutputDir ? "/../" + rootOutputDir : ""
  54. )};`;
  55. }
  56. /**
  57. * @returns {string | null} runtime code
  58. */
  59. generate() {
  60. const compilation = /** @type {Compilation} */ (this.compilation);
  61. const fn = RuntimeGlobals.ensureChunkHandlers;
  62. const withBaseURI = this.runtimeRequirements.has(RuntimeGlobals.baseURI);
  63. const withLoading = this.runtimeRequirements.has(
  64. RuntimeGlobals.ensureChunkHandlers
  65. );
  66. const withHmr = this.runtimeRequirements.has(
  67. RuntimeGlobals.hmrDownloadUpdateHandlers
  68. );
  69. const withHmrManifest = this.runtimeRequirements.has(
  70. RuntimeGlobals.hmrDownloadManifest
  71. );
  72. const globalObject = compilation.runtimeTemplate.globalObject;
  73. const chunkLoadingGlobalExpr = `${globalObject}[${JSON.stringify(
  74. compilation.outputOptions.chunkLoadingGlobal
  75. )}]`;
  76. const chunkGraph = /** @type {ChunkGraph} */ (this.chunkGraph);
  77. const chunk = /** @type {Chunk} */ (this.chunk);
  78. const hasJsMatcher = compileBooleanMatcher(
  79. chunkGraph.getChunkConditionMap(chunk, chunkHasJs)
  80. );
  81. const initialChunkIds = getInitialChunkIds(chunk, chunkGraph, chunkHasJs);
  82. const stateExpression = withHmr
  83. ? `${RuntimeGlobals.hmrRuntimeStatePrefix}_importScripts`
  84. : undefined;
  85. const runtimeTemplate = compilation.runtimeTemplate;
  86. const { _withCreateScriptUrl: withCreateScriptUrl } = this;
  87. return Template.asString([
  88. withBaseURI ? this._generateBaseUri(chunk) : "// no baseURI",
  89. "",
  90. "// object to store loaded chunks",
  91. '// "1" means "already loaded"',
  92. `var installedChunks = ${
  93. stateExpression ? `${stateExpression} = ${stateExpression} || ` : ""
  94. }{`,
  95. Template.indent(
  96. Array.from(initialChunkIds, id => `${JSON.stringify(id)}: 1`).join(
  97. ",\n"
  98. )
  99. ),
  100. "};",
  101. "",
  102. withLoading
  103. ? Template.asString([
  104. "// importScripts chunk loading",
  105. `var installChunk = ${runtimeTemplate.basicFunction("data", [
  106. runtimeTemplate.destructureArray(
  107. ["chunkIds", "moreModules", "runtime"],
  108. "data"
  109. ),
  110. "for(var moduleId in moreModules) {",
  111. Template.indent([
  112. `if(${RuntimeGlobals.hasOwnProperty}(moreModules, moduleId)) {`,
  113. Template.indent(
  114. `${RuntimeGlobals.moduleFactories}[moduleId] = moreModules[moduleId];`
  115. ),
  116. "}"
  117. ]),
  118. "}",
  119. `if(runtime) runtime(${RuntimeGlobals.require});`,
  120. "while(chunkIds.length)",
  121. Template.indent("installedChunks[chunkIds.pop()] = 1;"),
  122. "parentChunkLoadingFunction(data);"
  123. ])};`
  124. ])
  125. : "// no chunk install function needed",
  126. withLoading
  127. ? Template.asString([
  128. `${fn}.i = ${runtimeTemplate.basicFunction(
  129. "chunkId, promises",
  130. hasJsMatcher !== false
  131. ? [
  132. '// "1" is the signal for "already loaded"',
  133. "if(!installedChunks[chunkId]) {",
  134. Template.indent([
  135. hasJsMatcher === true
  136. ? "if(true) { // all chunks have JS"
  137. : `if(${hasJsMatcher("chunkId")}) {`,
  138. Template.indent(
  139. `importScripts(${
  140. withCreateScriptUrl
  141. ? `${RuntimeGlobals.createScriptUrl}(${RuntimeGlobals.publicPath} + ${RuntimeGlobals.getChunkScriptFilename}(chunkId))`
  142. : `${RuntimeGlobals.publicPath} + ${RuntimeGlobals.getChunkScriptFilename}(chunkId)`
  143. });`
  144. ),
  145. "}"
  146. ]),
  147. "}"
  148. ]
  149. : "installedChunks[chunkId] = 1;"
  150. )};`,
  151. "",
  152. `var chunkLoadingGlobal = ${chunkLoadingGlobalExpr} = ${chunkLoadingGlobalExpr} || [];`,
  153. "var parentChunkLoadingFunction = chunkLoadingGlobal.push.bind(chunkLoadingGlobal);",
  154. "chunkLoadingGlobal.push = installChunk;"
  155. ])
  156. : "// no chunk loading",
  157. "",
  158. withHmr
  159. ? Template.asString([
  160. "function loadUpdateChunk(chunkId, updatedModulesList) {",
  161. Template.indent([
  162. "var success = false;",
  163. `${globalObject}[${JSON.stringify(
  164. compilation.outputOptions.hotUpdateGlobal
  165. )}] = ${runtimeTemplate.basicFunction("_, moreModules, runtime", [
  166. "for(var moduleId in moreModules) {",
  167. Template.indent([
  168. `if(${RuntimeGlobals.hasOwnProperty}(moreModules, moduleId)) {`,
  169. Template.indent([
  170. "currentUpdate[moduleId] = moreModules[moduleId];",
  171. "if(updatedModulesList) updatedModulesList.push(moduleId);"
  172. ]),
  173. "}"
  174. ]),
  175. "}",
  176. "if(runtime) currentUpdateRuntime.push(runtime);",
  177. "success = true;"
  178. ])};`,
  179. "// start update chunk loading",
  180. `importScripts(${
  181. withCreateScriptUrl
  182. ? `${RuntimeGlobals.createScriptUrl}(${RuntimeGlobals.publicPath} + ${RuntimeGlobals.getChunkUpdateScriptFilename}(chunkId))`
  183. : `${RuntimeGlobals.publicPath} + ${RuntimeGlobals.getChunkUpdateScriptFilename}(chunkId)`
  184. });`,
  185. 'if(!success) throw new Error("Loading update chunk failed for unknown reason");'
  186. ]),
  187. "}",
  188. "",
  189. Template.getFunctionContent(
  190. require("../hmr/JavascriptHotModuleReplacement.runtime.js")
  191. )
  192. .replace(/\$key\$/g, "importScripts")
  193. .replace(/\$installedChunks\$/g, "installedChunks")
  194. .replace(/\$loadUpdateChunk\$/g, "loadUpdateChunk")
  195. .replace(/\$moduleCache\$/g, RuntimeGlobals.moduleCache)
  196. .replace(/\$moduleFactories\$/g, RuntimeGlobals.moduleFactories)
  197. .replace(
  198. /\$ensureChunkHandlers\$/g,
  199. RuntimeGlobals.ensureChunkHandlers
  200. )
  201. .replace(/\$hasOwnProperty\$/g, RuntimeGlobals.hasOwnProperty)
  202. .replace(/\$hmrModuleData\$/g, RuntimeGlobals.hmrModuleData)
  203. .replace(
  204. /\$hmrDownloadUpdateHandlers\$/g,
  205. RuntimeGlobals.hmrDownloadUpdateHandlers
  206. )
  207. .replace(
  208. /\$hmrInvalidateModuleHandlers\$/g,
  209. RuntimeGlobals.hmrInvalidateModuleHandlers
  210. )
  211. ])
  212. : "// no HMR",
  213. "",
  214. withHmrManifest
  215. ? Template.asString([
  216. `${
  217. RuntimeGlobals.hmrDownloadManifest
  218. } = ${runtimeTemplate.basicFunction("", [
  219. 'if (typeof fetch === "undefined") throw new Error("No browser support: need fetch API");',
  220. `return fetch(${RuntimeGlobals.publicPath} + ${
  221. RuntimeGlobals.getUpdateManifestFilename
  222. }()).then(${runtimeTemplate.basicFunction("response", [
  223. "if(response.status === 404) return; // no update available",
  224. 'if(!response.ok) throw new Error("Failed to fetch update manifest " + response.statusText);',
  225. "return response.json();"
  226. ])});`
  227. ])};`
  228. ])
  229. : "// no HMR manifest"
  230. ]);
  231. }
  232. }
  233. module.exports = ImportScriptsChunkLoadingRuntimeModule;