AsyncModuleRuntimeModule.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. */
  4. "use strict";
  5. const RuntimeGlobals = require("../RuntimeGlobals");
  6. const Template = require("../Template");
  7. const HelperRuntimeModule = require("./HelperRuntimeModule");
  8. /** @import Compilation from "../Compilation" */
  9. class AsyncModuleRuntimeModule extends HelperRuntimeModule {
  10. /**
  11. * @param {boolean=} deferInterop if defer import is used.
  12. */
  13. constructor(deferInterop = false) {
  14. super("async module");
  15. /** @type {boolean} */
  16. this._deferInterop = deferInterop;
  17. }
  18. /**
  19. * Generates runtime code for this runtime module.
  20. * @returns {string | null} runtime code
  21. */
  22. generate() {
  23. const compilation = /** @type {Compilation} */ (this.compilation);
  24. const { runtimeTemplate } = compilation;
  25. const fn = RuntimeGlobals.asyncModule;
  26. const defer = this._deferInterop;
  27. const cst = runtimeTemplate.renderConst();
  28. const lt = runtimeTemplate.renderLet();
  29. const supportsSymbol = runtimeTemplate.supportsSymbol();
  30. /** @type {(name: string, fallback: string) => string} */
  31. const renderSymbol = (name, fallback) =>
  32. supportsSymbol
  33. ? `Symbol("${name}")`
  34. : `hasSymbol ? Symbol("${name}") : "${fallback}"`;
  35. return Template.asString([
  36. ...(supportsSymbol
  37. ? []
  38. : [`${cst} hasSymbol = typeof Symbol === "function";`]),
  39. `${cst} webpackQueues = ${renderSymbol("webpack queues", "__webpack_queues__")};`,
  40. `${cst} webpackExports = ${
  41. defer ? `${RuntimeGlobals.asyncModuleExportSymbol}= ` : ""
  42. }${renderSymbol("webpack exports", RuntimeGlobals.exports)};`,
  43. `${cst} webpackError = ${renderSymbol("webpack error", "__webpack_error__")};`,
  44. defer
  45. ? Template.asString([
  46. `${cst} webpackDone = ${RuntimeGlobals.asyncModuleDoneSymbol} = ${renderSymbol("webpack done", "__webpack_done__")};`,
  47. `${cst} webpackDefer = ${RuntimeGlobals.deferredModuleAsyncTransitiveDependenciesSymbol} = ${renderSymbol("webpack defer", "__webpack_defer__")};`,
  48. `${RuntimeGlobals.deferredModuleAsyncTransitiveDependencies} = ${runtimeTemplate.basicFunction(
  49. "asyncDeps",
  50. [
  51. Template.indent([
  52. `${cst} hasUnresolvedAsyncSubgraph = asyncDeps.some((id) => {`,
  53. Template.indent([
  54. `${cst} cache = __webpack_module_cache__[id];`,
  55. "return !cache || cache[webpackDone] === false;"
  56. ]),
  57. "});",
  58. "if (hasUnresolvedAsyncSubgraph) {",
  59. Template.indent([
  60. "return ({ then(onFulfilled, onRejected) { return Promise.all(asyncDeps.map(__webpack_require__)).then(onFulfilled, onRejected) } })"
  61. ]),
  62. "}"
  63. ])
  64. ]
  65. )}`
  66. ])
  67. : "",
  68. `${cst} resolveQueue = ${runtimeTemplate.basicFunction("queue", [
  69. `if(${runtimeTemplate.optionalChaining("queue", "d < 1")}) {`,
  70. Template.indent([
  71. "queue.d = 1;",
  72. `queue.forEach(${runtimeTemplate.expressionFunction(
  73. "fn.r--",
  74. "fn"
  75. )});`,
  76. `queue.forEach(${runtimeTemplate.expressionFunction(
  77. "fn.r-- ? fn.r++ : fn()",
  78. "fn"
  79. )});`
  80. ]),
  81. "}"
  82. ])}`,
  83. `${cst} wrapDeps = ${runtimeTemplate.returningFunction(
  84. `deps.map(${runtimeTemplate.basicFunction("dep", [
  85. 'if(dep !== null && typeof dep === "object") {',
  86. Template.indent([
  87. defer
  88. ? Template.asString([
  89. "if(!dep[webpackQueues] && dep[webpackDefer]) {",
  90. Template.indent([
  91. `${cst} asyncDeps = ${RuntimeGlobals.deferredModuleAsyncTransitiveDependencies}(dep[webpackDefer]);`,
  92. "if (asyncDeps) {",
  93. Template.indent([
  94. `${cst} d = dep;`,
  95. "dep = {",
  96. Template.indent([
  97. "then(onFulfilled, onRejected) {",
  98. Template.indent([
  99. `asyncDeps.then(${runtimeTemplate.returningFunction(
  100. "onFulfilled(d)"
  101. )}, onRejected);`
  102. ]),
  103. "}"
  104. ]),
  105. "};"
  106. ]),
  107. "} else return dep;"
  108. ]),
  109. "}"
  110. ])
  111. : "",
  112. "if(dep[webpackQueues]) return dep;",
  113. "if(dep.then) {",
  114. Template.indent([
  115. `${cst} queue = [];`,
  116. "queue.d = 0;",
  117. `dep.then(${runtimeTemplate.basicFunction("r", [
  118. "obj[webpackExports] = r;",
  119. "resolveQueue(queue);"
  120. ])}, ${runtimeTemplate.basicFunction("e", [
  121. "obj[webpackError] = e;",
  122. "resolveQueue(queue);"
  123. ])});`,
  124. `${cst} obj = {};`,
  125. defer ? "obj[webpackDefer] = false;" : "",
  126. `obj[webpackQueues] = ${runtimeTemplate.expressionFunction(
  127. "fn(queue)",
  128. "fn"
  129. )};`,
  130. "return obj;"
  131. ]),
  132. "}"
  133. ]),
  134. "}",
  135. `${cst} ret = {};`,
  136. `ret[webpackQueues] = ${runtimeTemplate.emptyFunction()};`,
  137. "ret[webpackExports] = dep;",
  138. "return ret;"
  139. ])})`,
  140. "deps"
  141. )};`,
  142. `${fn} = ${runtimeTemplate.basicFunction("module, body, hasAwait", [
  143. `${lt} queue;`,
  144. "hasAwait && ((queue = []).d = -1);",
  145. `${cst} depQueues = new Set();`,
  146. `${cst} exports = module.exports;`,
  147. `${lt} currentDeps;`,
  148. `${lt} outerResolve;`,
  149. `${lt} reject;`,
  150. `${cst} promise = new Promise(${runtimeTemplate.basicFunction(
  151. "resolve, rej",
  152. ["reject = rej;", "outerResolve = resolve;"]
  153. )});`,
  154. "promise[webpackExports] = exports;",
  155. `promise[webpackQueues] = ${runtimeTemplate.expressionFunction(
  156. `queue && fn(queue), depQueues.forEach(fn), promise["catch"](${runtimeTemplate.emptyFunction()})`,
  157. "fn"
  158. )};`,
  159. "module.exports = promise;",
  160. `${cst} handle = ${runtimeTemplate.basicFunction("deps", [
  161. "currentDeps = wrapDeps(deps);",
  162. `${lt} fn;`,
  163. `${cst} getResult = ${runtimeTemplate.returningFunction(
  164. `currentDeps.map(${runtimeTemplate.basicFunction("d", [
  165. defer ? "if(d[webpackDefer]) return d;" : "",
  166. "if(d[webpackError]) throw d[webpackError];",
  167. "return d[webpackExports];"
  168. ])})`
  169. )}`,
  170. `${cst} promise = new Promise(${runtimeTemplate.basicFunction(
  171. "resolve",
  172. [
  173. `fn = ${runtimeTemplate.expressionFunction(
  174. "resolve(getResult)",
  175. ""
  176. )};`,
  177. "fn.r = 0;",
  178. `${cst} fnQueue = ${runtimeTemplate.expressionFunction(
  179. "q !== queue && !depQueues.has(q) && (depQueues.add(q), q && !q.d && (fn.r++, q.push(fn)))",
  180. "q"
  181. )};`,
  182. `currentDeps.forEach(${runtimeTemplate.expressionFunction(
  183. `${
  184. defer ? "dep[webpackDefer]||" : ""
  185. }dep[webpackQueues](fnQueue)`,
  186. "dep"
  187. )});`
  188. ]
  189. )});`,
  190. "return fn.r ? promise : getResult();"
  191. ])}`,
  192. `${cst} done = ${runtimeTemplate.expressionFunction(
  193. `(err ? reject(promise[webpackError] = err) : outerResolve(exports)), resolveQueue(queue)${
  194. defer
  195. ? ", promise[webpackDone] = true, module.evaluatingAsync = false"
  196. : ""
  197. }`,
  198. "err"
  199. )}`,
  200. // Track the async body's whole lifetime (evaluating +
  201. // evaluating-async states) with a dedicated flag — the sync require
  202. // wrapper clears `module.evaluating` as soon as `.a` returns, so a
  203. // deferred namespace reaching this module through a cycle relies on
  204. // `evaluatingAsync` to keep throwing until it is fully evaluated.
  205. defer ? "module.evaluatingAsync = true;" : "",
  206. "body(handle, done);",
  207. `${runtimeTemplate.optionalChaining("queue", "d < 0")} && (queue.d = 0);`
  208. ])};`
  209. ]);
  210. }
  211. }
  212. module.exports = AsyncModuleRuntimeModule;