MakeDeferredNamespaceObjectRuntime.js 7.4 KB

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