MakeDeferredNamespaceObjectRuntime.js 8.0 KB

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