MakeDeferredNamespaceObjectRuntime.js 8.1 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 Template = require("../Template");
  7. const HelperRuntimeModule = require("./HelperRuntimeModule");
  8. /** @typedef {import("../Module").RuntimeRequirements} RuntimeRequirements */
  9. /** @typedef {import("../Module").ExportsType} ExportsType */
  10. /** @typedef {import("../ChunkGraph").ModuleId} ModuleId */
  11. /**
  12. * @param {ExportsType} exportsType exports type
  13. * @returns {string} mode
  14. */
  15. function getMakeDeferredNamespaceModeFromExportsType(exportsType) {
  16. // number is from createFakeNamespaceObject mode ^ 1
  17. if (exportsType === "namespace") return `/* ${exportsType} */ 8`;
  18. if (exportsType === "default-only") return `/* ${exportsType} */ 0`;
  19. if (exportsType === "default-with-named") return `/* ${exportsType} */ 2`;
  20. if (exportsType === "dynamic") return `/* ${exportsType} */ 6`;
  21. throw new Error(`Unknown exports type: ${exportsType}`);
  22. }
  23. /**
  24. * @param {string} moduleId moduleId
  25. * @param {ExportsType} exportsType exportsType
  26. * @param {(ModuleId | null)[]} asyncDepsIds asyncDepsIds
  27. * @param {RuntimeRequirements} runtimeRequirements runtime requirements
  28. * @returns {string} call make optimized deferred namespace object
  29. */
  30. function getOptimizedDeferredModule(
  31. moduleId,
  32. exportsType,
  33. asyncDepsIds,
  34. runtimeRequirements
  35. ) {
  36. runtimeRequirements.add(RuntimeGlobals.makeOptimizedDeferredNamespaceObject);
  37. const mode = getMakeDeferredNamespaceModeFromExportsType(exportsType);
  38. return `${RuntimeGlobals.makeOptimizedDeferredNamespaceObject}(${moduleId}, ${mode}${
  39. asyncDepsIds.length > 0
  40. ? `, ${JSON.stringify(asyncDepsIds.filter((x) => x !== null))}`
  41. : ""
  42. })`;
  43. }
  44. class MakeOptimizedDeferredNamespaceObjectRuntimeModule extends HelperRuntimeModule {
  45. /**
  46. * @param {boolean} hasAsyncRuntime if async module is used.
  47. */
  48. constructor(hasAsyncRuntime) {
  49. super("make optimized deferred namespace object");
  50. /** @type {boolean} */
  51. this.hasAsyncRuntime = hasAsyncRuntime;
  52. }
  53. /**
  54. * Generates runtime code for this runtime module.
  55. * @returns {string | null} runtime code
  56. */
  57. generate() {
  58. if (!this.compilation) return null;
  59. const fn = RuntimeGlobals.makeOptimizedDeferredNamespaceObject;
  60. const hasAsync = this.hasAsyncRuntime;
  61. return Template.asString([
  62. // Note: must be a function (not arrow), because this is used in body!
  63. `${fn} = function(moduleId, mode${hasAsync ? ", asyncDeps" : ""}) {`,
  64. Template.indent([
  65. "var r = this;",
  66. hasAsync ? "var isAsync = asyncDeps && asyncDeps.length;" : "",
  67. "var obj = {",
  68. Template.indent([
  69. "get a() {",
  70. Template.indent([
  71. "var exports = r(moduleId);",
  72. hasAsync
  73. ? `if(isAsync) exports = exports[${RuntimeGlobals.asyncModuleExportSymbol}];`
  74. : "",
  75. // if exportsType is "namespace" we can generate the most optimized code,
  76. // on the second access, we can avoid trigger the getter.
  77. // we can also do this if exportsType is "dynamic" and there is a "__esModule" property on it.
  78. 'if(mode & 8 || (mode & 4 && exports.__esModule)) Object.defineProperty(this, "a", { value: exports });',
  79. "return exports;"
  80. ]),
  81. "}"
  82. ]),
  83. "};",
  84. hasAsync
  85. ? `if(isAsync) obj[${RuntimeGlobals.deferredModuleAsyncTransitiveDependenciesSymbol}] = asyncDeps;`
  86. : "",
  87. "return obj;"
  88. ]),
  89. "};"
  90. ]);
  91. }
  92. }
  93. class MakeDeferredNamespaceObjectRuntimeModule extends HelperRuntimeModule {
  94. /**
  95. * @param {boolean} hasAsyncRuntime if async module is used.
  96. */
  97. constructor(hasAsyncRuntime) {
  98. super("make deferred namespace object");
  99. /** @type {boolean} */
  100. this.hasAsyncRuntime = hasAsyncRuntime;
  101. }
  102. /**
  103. * Generates runtime code for this runtime module.
  104. * @returns {string | null} runtime code
  105. */
  106. generate() {
  107. if (!this.compilation) return null;
  108. const { runtimeTemplate } = this.compilation;
  109. const fn = RuntimeGlobals.makeDeferredNamespaceObject;
  110. const hasAsync = this.hasAsyncRuntime;
  111. const init = runtimeTemplate.supportsOptionalChaining()
  112. ? "init?.();"
  113. : "if (init) init();";
  114. return `${fn} = ${runtimeTemplate.basicFunction("moduleId, mode", [
  115. "var cachedModule = __webpack_module_cache__[moduleId];",
  116. "if (cachedModule && cachedModule.error === undefined) {",
  117. Template.indent([
  118. "var exports = cachedModule.exports;",
  119. hasAsync
  120. ? `if (${RuntimeGlobals.asyncModuleExportSymbol} in exports) exports = exports[${RuntimeGlobals.asyncModuleExportSymbol}];`
  121. : "",
  122. "if (mode & 8) return exports;",
  123. `return ${RuntimeGlobals.createFakeNamespaceObject}(exports, mode);`
  124. ]),
  125. "}",
  126. "",
  127. `var init = ${runtimeTemplate.basicFunction("", [
  128. `ns = ${RuntimeGlobals.require}(moduleId);`,
  129. hasAsync
  130. ? `if (${RuntimeGlobals.asyncModuleExportSymbol} in ns) ns = ns[${RuntimeGlobals.asyncModuleExportSymbol}];`
  131. : "",
  132. "init = null;",
  133. "if (mode & 8 || mode & 4 && ns.__esModule && typeof ns === 'object') {",
  134. Template.indent([
  135. "delete handler.defineProperty;",
  136. "delete handler.deleteProperty;",
  137. "delete handler.set;",
  138. "delete handler.get;",
  139. "delete handler.has;",
  140. "delete handler.ownKeys;",
  141. "delete handler.getOwnPropertyDescriptor;"
  142. ]),
  143. "} else {",
  144. Template.indent([
  145. `ns = ${RuntimeGlobals.createFakeNamespaceObject}(ns, mode);`
  146. ]),
  147. "}"
  148. ])};`,
  149. "",
  150. "var ns = __webpack_module_deferred_exports__[moduleId] || (__webpack_module_deferred_exports__[moduleId] = { __proto__: null });",
  151. "var handler = {",
  152. Template.indent([
  153. "__proto__: null,",
  154. `get: ${runtimeTemplate.basicFunction("_, name", [
  155. "switch (name) {",
  156. Template.indent([
  157. 'case "__esModule": return true;',
  158. 'case Symbol.toStringTag: return "Deferred Module";',
  159. 'case "then": return undefined;'
  160. ]),
  161. "}",
  162. init,
  163. "return ns[name];"
  164. ])},`,
  165. `has: ${runtimeTemplate.basicFunction("_, name", [
  166. "switch (name) {",
  167. Template.indent(
  168. [
  169. 'case "__esModule":',
  170. "case Symbol.toStringTag:",
  171. hasAsync
  172. ? `case ${RuntimeGlobals.deferredModuleAsyncTransitiveDependenciesSymbol}:`
  173. : "",
  174. Template.indent("return true;"),
  175. 'case "then":',
  176. Template.indent("return false;")
  177. ].filter(Boolean)
  178. ),
  179. "}",
  180. init,
  181. "return name in ns;"
  182. ])},`,
  183. `ownKeys: ${runtimeTemplate.basicFunction("", [
  184. init,
  185. `var keys = Reflect.ownKeys(ns).filter(${runtimeTemplate.expressionFunction('x !== "then" && x !== Symbol.toStringTag', "x")}).concat([Symbol.toStringTag]);`,
  186. "return keys;"
  187. ])},`,
  188. `getOwnPropertyDescriptor: ${runtimeTemplate.basicFunction("_, name", [
  189. "switch (name) {",
  190. Template.indent([
  191. 'case "__esModule": return { value: true, configurable: !(mode & 8) };',
  192. 'case Symbol.toStringTag: return { value: "Deferred Module", configurable: !(mode & 8) };',
  193. 'case "then": return undefined;'
  194. ]),
  195. "}",
  196. init,
  197. "var desc = Reflect.getOwnPropertyDescriptor(ns, name);",
  198. 'if (mode & 2 && name == "default" && !desc) {',
  199. Template.indent("desc = { value: ns, configurable: true };"),
  200. "}",
  201. "return desc;"
  202. ])},`,
  203. `defineProperty: ${runtimeTemplate.basicFunction("_, name", [
  204. init,
  205. // Note: This behavior does not match the spec one, but since webpack does not do it either
  206. // for a normal Module Namespace object (in MakeNamespaceObjectRuntimeModule), let's keep it simple.
  207. "return false;"
  208. ])},`,
  209. `deleteProperty: ${runtimeTemplate.returningFunction("false")},`,
  210. `set: ${runtimeTemplate.returningFunction("false")},`
  211. ]),
  212. "}",
  213. // we don't fully emulate ES Module semantics in this Proxy to align with normal webpack esm namespace object.
  214. "return new Proxy(ns, handler);"
  215. ])};`;
  216. }
  217. }
  218. module.exports.MakeDeferredNamespaceObjectRuntimeModule =
  219. MakeDeferredNamespaceObjectRuntimeModule;
  220. module.exports.MakeOptimizedDeferredNamespaceObjectRuntimeModule =
  221. MakeOptimizedDeferredNamespaceObjectRuntimeModule;
  222. module.exports.getMakeDeferredNamespaceModeFromExportsType =
  223. getMakeDeferredNamespaceModeFromExportsType;
  224. module.exports.getOptimizedDeferredModule = getOptimizedDeferredModule;