MakeDeferredNamespaceObjectRuntime.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566
  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 HarmonyImportDependency = require("../dependencies/HarmonyImportDependency");
  8. const HelperRuntimeModule = require("./HelperRuntimeModule");
  9. /** @import { ModuleId } from "../ChunkGraph" */
  10. /** @import Module, { RuntimeRequirements, ExportsType } from "../Module" */
  11. /** @import ModuleGraph from "../ModuleGraph" */
  12. /**
  13. * @typedef {object} DeferredCycleState
  14. * @property {Map<Module, Set<Module>>} sccOf strongly-connected component per module (shared object per component)
  15. * @property {Map<Module, Set<Module> | null>} peers memoized SCC peers (component minus the module) per deferred module
  16. */
  17. /** @type {WeakMap<ModuleGraph, DeferredCycleState>} */
  18. const deferredCycleCache = new WeakMap();
  19. /**
  20. * Materializes the active harmony-import targets of a module (deferred edges
  21. * included, matching the spec's `RequestedModules` recursion).
  22. * @param {ModuleGraph} moduleGraph the module graph
  23. * @param {Module} module the module
  24. * @returns {Module[]} imported modules
  25. */
  26. function harmonyImportTargets(moduleGraph, module) {
  27. const connections = moduleGraph.getOutgoingConnectionsByModule(module);
  28. if (!connections) return [];
  29. /** @type {Module[]} */
  30. const targets = [];
  31. for (const [dep, moduleConnections] of connections) {
  32. if (
  33. dep &&
  34. moduleConnections.some(
  35. (c) =>
  36. c.dependency instanceof HarmonyImportDependency &&
  37. c.isTargetActive(undefined)
  38. )
  39. ) {
  40. targets.push(dep);
  41. }
  42. }
  43. return targets;
  44. }
  45. /**
  46. * Assigns every module reachable from `seed` (through active harmony imports)
  47. * to its strongly-connected component via an iterative Tarjan pass, writing the
  48. * results into `sccOf`. Modules already assigned by an earlier pass are treated
  49. * as finished, so across all deferred imports every edge is visited once
  50. * (O(V + E) total) rather than re-walked per import.
  51. * @param {ModuleGraph} moduleGraph the module graph
  52. * @param {Module} seed the module to explore from
  53. * @param {Map<Module, Set<Module>>} sccOf module-to-component map to populate
  54. * @returns {void}
  55. */
  56. function assignStronglyConnectedComponents(moduleGraph, seed, sccOf) {
  57. if (sccOf.has(seed)) return;
  58. /** @type {Map<Module, number>} */
  59. const index = new Map();
  60. /** @type {Map<Module, number>} */
  61. const low = new Map();
  62. /** @type {Set<Module>} */
  63. const onStack = new Set();
  64. /** @type {Module[]} */
  65. const componentStack = [];
  66. /** @type {{ node: Module, targets: Module[], i: number }[]} */
  67. const work = [];
  68. let counter = 0;
  69. /**
  70. * @param {Module} node node to open
  71. */
  72. const open = (node) => {
  73. index.set(node, counter);
  74. low.set(node, counter);
  75. counter++;
  76. componentStack.push(node);
  77. onStack.add(node);
  78. work.push({ node, targets: harmonyImportTargets(moduleGraph, node), i: 0 });
  79. };
  80. open(seed);
  81. while (work.length > 0) {
  82. const frame = work[work.length - 1];
  83. if (frame.i < frame.targets.length) {
  84. const next = frame.targets[frame.i++];
  85. // Already in a finished component (from this or a prior pass): skip.
  86. if (sccOf.has(next)) continue;
  87. if (!index.has(next)) {
  88. open(next);
  89. } else if (onStack.has(next)) {
  90. const nodeLow = /** @type {number} */ (low.get(frame.node));
  91. const nextIndex = /** @type {number} */ (index.get(next));
  92. if (nextIndex < nodeLow) low.set(frame.node, nextIndex);
  93. }
  94. continue;
  95. }
  96. work.pop();
  97. const node = frame.node;
  98. const nodeLow = /** @type {number} */ (low.get(node));
  99. if (work.length > 0) {
  100. const parent = work[work.length - 1].node;
  101. if (nodeLow < /** @type {number} */ (low.get(parent))) {
  102. low.set(parent, nodeLow);
  103. }
  104. }
  105. if (nodeLow === index.get(node)) {
  106. /** @type {Set<Module>} */
  107. const component = new Set();
  108. let member;
  109. do {
  110. member = /** @type {Module} */ (componentStack.pop());
  111. onStack.delete(member);
  112. component.add(member);
  113. } while (member !== node);
  114. for (const m of component) sccOf.set(m, component);
  115. }
  116. }
  117. }
  118. /**
  119. * Per the TC39 import-defer spec's `ReadyForSyncExecution`, forcing evaluation
  120. * of a deferred module must throw when any module in its transitive static
  121. * import closure is currently evaluating. Only a module in `module`'s own
  122. * strongly-connected component can be evaluating at that point (anything else it
  123. * imports is either already evaluated or not started), so we emit just the SCC
  124. * peers instead of the whole (potentially huge) forward closure.
  125. * @param {ModuleGraph} moduleGraph the module graph
  126. * @param {Module} module the deferred module
  127. * @returns {Set<Module> | null} the SCC peers when cyclic, otherwise null
  128. */
  129. function getDeferredCycleModules(moduleGraph, module) {
  130. let state = deferredCycleCache.get(moduleGraph);
  131. if (state === undefined) {
  132. state = { sccOf: new Map(), peers: new Map() };
  133. deferredCycleCache.set(moduleGraph, state);
  134. }
  135. const cached = state.peers.get(module);
  136. if (cached !== undefined) return cached;
  137. assignStronglyConnectedComponents(moduleGraph, module, state.sccOf);
  138. const component = state.sccOf.get(module);
  139. // A trivial component (size 1, no self-cycle peers) means no cycle; a direct
  140. // self-loop is already covered by the `evaluating` check on `module` itself.
  141. let result = null;
  142. if (component !== undefined && component.size > 1) {
  143. result = new Set(component);
  144. result.delete(module);
  145. }
  146. state.peers.set(module, result);
  147. return result;
  148. }
  149. /**
  150. * Maps a defer-cycle closure to the runtime module ids whose `evaluating` flag
  151. * the deferred namespace must check. `resolveId` maps a closure module to the
  152. * runtime id that carries its evaluation state (its own id, or the id of the
  153. * concatenated module that absorbed it); unresolved (`null`) members are
  154. * dropped. Returns `null` when there is nothing to check.
  155. * @param {Set<Module> | null} closure closure modules, or null when not cyclic
  156. * @param {(module: Module) => ModuleId | null} resolveId maps a module to its runtime id
  157. * @returns {ModuleId[] | null} deduplicated runtime ids, or null
  158. */
  159. function getDeferredCycleModuleIds(closure, resolveId) {
  160. if (closure === null) return null;
  161. /** @type {Set<ModuleId>} */
  162. const ids = new Set();
  163. for (const module of closure) {
  164. const id = resolveId(module);
  165. if (id !== null) ids.add(id);
  166. }
  167. return ids.size > 0 ? [...ids] : null;
  168. }
  169. /**
  170. * @param {ExportsType} exportsType exports type
  171. * @returns {string} mode
  172. */
  173. function getMakeDeferredNamespaceModeFromExportsType(exportsType) {
  174. // number is from createFakeNamespaceObject mode ^ 1
  175. if (exportsType === "namespace") return `/* ${exportsType} */ 8`;
  176. if (exportsType === "default-only") return `/* ${exportsType} */ 0`;
  177. if (exportsType === "default-with-named") return `/* ${exportsType} */ 2`;
  178. if (exportsType === "dynamic") return `/* ${exportsType} */ 6`;
  179. throw new Error(`Unknown exports type: ${exportsType}`);
  180. }
  181. /**
  182. * @param {string} moduleId moduleId
  183. * @param {ExportsType} exportsType exportsType
  184. * @param {(ModuleId | null)[]} asyncDepsIds asyncDepsIds
  185. * @param {ModuleId[] | null} syncCycleDepsIds transitive static closure ids when the module is part of a defer cycle
  186. * @param {RuntimeRequirements} runtimeRequirements runtime requirements
  187. * @returns {string} call make optimized deferred namespace object
  188. */
  189. function getOptimizedDeferredModule(
  190. moduleId,
  191. exportsType,
  192. asyncDepsIds,
  193. syncCycleDepsIds,
  194. runtimeRequirements
  195. ) {
  196. runtimeRequirements.add(RuntimeGlobals.makeOptimizedDeferredNamespaceObject);
  197. const mode = getMakeDeferredNamespaceModeFromExportsType(exportsType);
  198. const asyncDeps = asyncDepsIds.filter((x) => x !== null);
  199. const hasSync = syncCycleDepsIds !== null && syncCycleDepsIds.length > 0;
  200. // `syncDeps` is passed positionally after `asyncDeps`, so a `0` placeholder
  201. // keeps the slot when the module has cycle deps but no async deps.
  202. const args = [moduleId, mode];
  203. if (asyncDeps.length > 0 || hasSync) {
  204. args.push(asyncDeps.length > 0 ? JSON.stringify(asyncDeps) : "0");
  205. }
  206. if (hasSync) args.push(JSON.stringify(syncCycleDepsIds));
  207. return `${RuntimeGlobals.makeOptimizedDeferredNamespaceObject}(${args.join(
  208. ", "
  209. )})`;
  210. }
  211. class MakeOptimizedDeferredNamespaceObjectRuntimeModule extends HelperRuntimeModule {
  212. /**
  213. * Returns true, if the runtime module should get it's own scope.
  214. * When false, `generate()` must emit complete statements ending with `;`
  215. * so a following runtime IIFE is not parsed as a call (ASI).
  216. * @returns {boolean} true, if the runtime module should get it's own scope
  217. */
  218. shouldIsolate() {
  219. return false;
  220. }
  221. /**
  222. * @param {boolean} hasAsyncRuntime if async module is used.
  223. */
  224. constructor(hasAsyncRuntime) {
  225. super("make optimized deferred namespace object");
  226. /** @type {boolean} */
  227. this.hasAsyncRuntime = hasAsyncRuntime;
  228. }
  229. /**
  230. * Generates runtime code for this runtime module.
  231. * @returns {string | null} runtime code
  232. */
  233. generate() {
  234. if (!this.compilation) return null;
  235. const { runtimeTemplate } = this.compilation;
  236. const cst = runtimeTemplate.renderConst();
  237. const lt = runtimeTemplate.renderLet();
  238. const fn = RuntimeGlobals.makeOptimizedDeferredNamespaceObject;
  239. const hasAsync = this.hasAsyncRuntime;
  240. return Template.asString([
  241. // Note: must be a function (not arrow), because this is used in body!
  242. // `asyncDeps` keeps a fixed positional slot even without async runtime
  243. // so the trailing `syncDeps` (defer-cycle closure) always lines up.
  244. `${fn} = function(moduleId, mode, asyncDeps, syncDeps) {`,
  245. Template.indent([
  246. `${cst} r = this;`,
  247. hasAsync ? `${cst} isAsync = asyncDeps && asyncDeps.length;` : "",
  248. `${cst} obj = {`,
  249. Template.indent([
  250. "get a() {",
  251. Template.indent([
  252. // Forcing evaluation of a module that is currently evaluating
  253. // (a cycle reached through a deferred import) must throw rather
  254. // than expose its partial exports. `syncDeps` (present only for
  255. // cyclic deferred modules) carries the transitive static closure,
  256. // so an evaluating dependency is caught before any evaluation.
  257. `${cst} cachedModule = __webpack_module_cache__[moduleId];`,
  258. 'if (cachedModule !== undefined && (cachedModule.evaluating || cachedModule.evaluatingAsync)) throw new TypeError("Cannot access a deferred module namespace while the module is being evaluated");',
  259. "if (syncDeps) for (var i = 0; i < syncDeps.length; i++) {",
  260. Template.indent([
  261. `${cst} depModule = __webpack_module_cache__[syncDeps[i]];`,
  262. 'if (depModule !== undefined && (depModule.evaluating || depModule.evaluatingAsync)) throw new TypeError("Cannot access a deferred module namespace while a dependency is being evaluated");'
  263. ]),
  264. "}",
  265. `${lt} exports = r(moduleId);`,
  266. hasAsync
  267. ? `if(isAsync) exports = exports[${RuntimeGlobals.asyncModuleExportSymbol}];`
  268. : "",
  269. // if exportsType is "namespace" we can generate the most optimized code,
  270. // on the second access, we can avoid trigger the getter.
  271. // we can also do this if exportsType is "dynamic" and there is a "__esModule" property on it.
  272. 'if(mode & 8 || (mode & 4 && exports.__esModule)) Object.defineProperty(this, "a", { value: exports });',
  273. "return exports;"
  274. ]),
  275. "}"
  276. ]),
  277. "};",
  278. hasAsync
  279. ? `if(isAsync) obj[${RuntimeGlobals.deferredModuleAsyncTransitiveDependenciesSymbol}] = asyncDeps;`
  280. : "",
  281. "return obj;"
  282. ]),
  283. "};"
  284. ]);
  285. }
  286. }
  287. class MakeDeferredNamespaceObjectRuntimeModule extends HelperRuntimeModule {
  288. /**
  289. * Returns true, if the runtime module should get it's own scope.
  290. * When false, `generate()` must emit complete statements ending with `;`
  291. * so a following runtime IIFE is not parsed as a call (ASI).
  292. * @returns {boolean} true, if the runtime module should get it's own scope
  293. */
  294. shouldIsolate() {
  295. return false;
  296. }
  297. /**
  298. * @param {boolean} hasAsyncRuntime if async module is used.
  299. */
  300. constructor(hasAsyncRuntime) {
  301. super("make deferred namespace object");
  302. /** @type {boolean} */
  303. this.hasAsyncRuntime = hasAsyncRuntime;
  304. }
  305. /**
  306. * Generates runtime code for this runtime module.
  307. * @returns {string | null} runtime code
  308. */
  309. generate() {
  310. if (!this.compilation) return null;
  311. const { runtimeTemplate } = this.compilation;
  312. const cst = runtimeTemplate.renderConst();
  313. const lt = runtimeTemplate.renderLet();
  314. const fn = RuntimeGlobals.makeDeferredNamespaceObject;
  315. const hasAsync = this.hasAsyncRuntime;
  316. const init = `${runtimeTemplate.optionalChaining("init", "()")};`;
  317. return `${fn} = ${runtimeTemplate.basicFunction("moduleId, mode", [
  318. // Per the TC39 import-defer spec, deferred namespaces are
  319. // distinct from their eager counterparts and the same module
  320. // referenced from multiple defer-import sites must yield the
  321. // same object. Cache the Proxy / fake namespace per-moduleId so
  322. // repeated calls (including across files) share identity.
  323. //
  324. // Bit 16 (`createFakeNamespaceObject`'s "return value when
  325. // it's Promise-like" flag added by
  326. // `RuntimeTemplate.moduleNamespacePromise` for dynamic
  327. // imports) is irrelevant for deferred namespaces — the value
  328. // passed into `createFakeNamespaceObject` here is always the
  329. // resolved module exports (after unwrapping the async-module
  330. // export symbol when present), never a Promise. Strip it
  331. // once so all downstream behavior, the cache key, and the
  332. // `createFakeNamespaceObject` call below see the same shape
  333. // mode. This keeps static defer (mode 8) and dynamic
  334. // `await import.defer` (mode 8 | 16) sharing the same
  335. // Deferred Module Namespace object, while still keying by
  336. // `(moduleId, mode)` so distinct exports-type shapes
  337. // (e.g. one importer treats a CJS module as
  338. // "default-with-named", another as "namespace") get
  339. // distinct cache entries.
  340. "mode &= ~16;",
  341. `${lt} byMode = __webpack_module_deferred_namespace_cache__[moduleId];`,
  342. "if (byMode && byMode[mode] !== undefined) return byMode[mode];",
  343. "if (!byMode) byMode = __webpack_module_deferred_namespace_cache__[moduleId] = {};",
  344. `${cst} cachedModule = __webpack_module_cache__[moduleId];`,
  345. "if (cachedModule && cachedModule.error === undefined && !(mode & 8)) {",
  346. Template.indent([
  347. `${lt} exports = cachedModule.exports;`,
  348. hasAsync
  349. ? `if (${RuntimeGlobals.asyncModuleExportSymbol} in exports) exports = exports[${RuntimeGlobals.asyncModuleExportSymbol}];`
  350. : "",
  351. `return byMode[mode] = ${RuntimeGlobals.createFakeNamespaceObject}(exports, mode);`
  352. ]),
  353. "}",
  354. "",
  355. `${lt} init = ${runtimeTemplate.basicFunction("", [
  356. // A deferred namespace that forces evaluation of a module that is
  357. // already evaluating (a cycle) must throw rather than expose its
  358. // partial exports.
  359. `${cst} evaluatingModule = __webpack_module_cache__[moduleId];`,
  360. 'if (evaluatingModule !== undefined && (evaluatingModule.evaluating || evaluatingModule.evaluatingAsync)) throw new TypeError("Cannot access a deferred module namespace while the module is being evaluated");',
  361. `ns = ${RuntimeGlobals.require}(moduleId);`,
  362. hasAsync
  363. ? `if (${RuntimeGlobals.asyncModuleExportSymbol} in ns) ns = ns[${RuntimeGlobals.asyncModuleExportSymbol}];`
  364. : "",
  365. "init = null;",
  366. "if (mode & 8 || mode & 4 && ns.__esModule && typeof ns === 'object') {",
  367. Template.indent([
  368. // Drop only the read-side traps after init: with the
  369. // resolved namespace's own keys mirrored onto
  370. // `ns_target` below, the default `Reflect` behavior
  371. // returns the right values via the live-binding
  372. // getters, so we no longer need to intercept `get` /
  373. // `has` / `ownKeys` / `getOwnPropertyDescriptor`.
  374. //
  375. // The mutation traps (`set`, `deleteProperty`,
  376. // `defineProperty`) are kept because per the TC39
  377. // import-defer spec, `[[Set]]` / `[[Delete]]` /
  378. // `[[DefineOwnProperty]]` on a Deferred Module
  379. // Namespace Exotic Object never succeed — and the
  380. // proxy target itself remains extensible
  381. // (architecturally we cannot freeze it up-front),
  382. // so without these traps `ns.notExported = "x"`
  383. // after evaluation would silently create a property
  384. // on the target instead of returning false.
  385. "delete handler.get;",
  386. "delete handler.has;",
  387. "delete handler.ownKeys;",
  388. "delete handler.getOwnPropertyDescriptor;"
  389. ]),
  390. "} else {",
  391. Template.indent([
  392. `ns = ${RuntimeGlobals.createFakeNamespaceObject}(ns, mode);`
  393. ]),
  394. "}",
  395. // Mirror own properties from the resolved namespace onto the proxy
  396. // target so that proxy invariants hold for callers that structurally
  397. // introspect via `Object.keys` / `Object.getOwnPropertyNames` /
  398. // `Object.getOwnPropertyDescriptor`: when our trap reports a
  399. // non-configurable descriptor for a key, the target must also have
  400. // that key with a matching descriptor.
  401. //
  402. // `__esModule` and `Symbol.toStringTag` are intentionally skipped:
  403. // the proxy synthesizes "Deferred Module" / true regardless of what
  404. // the underlying namespace exposes (per the TC39 import-defer
  405. // proposal, the [[StringTag]] of a Deferred Module Namespace
  406. // Exotic Object is "Deferred Module"), and the target was already
  407. // pre-populated with those values below.
  408. `${cst} keys = Reflect.ownKeys(ns);`,
  409. "for (var i = 0; i < keys.length; i++) {",
  410. Template.indent([
  411. `${cst} k = keys[i];`,
  412. 'if (k === "__esModule" || k === Symbol.toStringTag) continue;',
  413. `if (!${runtimeTemplate.objectHasOwn("ns_target", "k")}) {`,
  414. Template.indent([
  415. "try { Object.defineProperty(ns_target, k, Reflect.getOwnPropertyDescriptor(ns, k)); } catch (_) {}"
  416. ]),
  417. "}"
  418. ]),
  419. "}"
  420. ])};`,
  421. "",
  422. // The proxy target is a fresh placeholder, separate from
  423. // `__webpack_module_deferred_exports__[moduleId]` (which is reused
  424. // by `__webpack_require__` as `module.exports` for deferred-loaded
  425. // modules and would conflict with our pre-populated synthetic
  426. // `__esModule` / `Symbol.toStringTag` non-configurable properties).
  427. // Using a dedicated target keeps the proxy invariant-compliant
  428. // without interfering with the module's own exports object.
  429. `${cst} ns_target = { __proto__: null };`,
  430. // Pre-populate the synthetic deferred-namespace properties with
  431. // fully non-configurable, non-writable, non-enumerable descriptors
  432. // (matching the TC39 import-defer spec for Module Namespace
  433. // Exotic Objects). The trap returns the same descriptors below.
  434. 'Object.defineProperty(ns_target, "__esModule", { value: true });',
  435. 'Object.defineProperty(ns_target, Symbol.toStringTag, { value: "Deferred Module" });',
  436. `${lt} ns = ns_target;`,
  437. `${cst} handler = {`,
  438. Template.indent([
  439. "__proto__: null,",
  440. // Per the TC39 import-defer proposal, `IsSymbolLikeNamespaceKey`
  441. // returns true for any Symbol-keyed access (and for "then"); such
  442. // accesses go through `OrdinaryGetOwnProperty` and must not
  443. // trigger evaluation of the deferred module. The Symbol checks
  444. // below short-circuit to the pre-populated target without
  445. // running `init()`.
  446. `${runtimeTemplate.method("get", "_, name", [
  447. "switch (name) {",
  448. Template.indent([
  449. 'case "__esModule": return true;',
  450. 'case Symbol.toStringTag: return "Deferred Module";',
  451. 'case "then": return undefined;'
  452. ]),
  453. "}",
  454. 'if (typeof name === "symbol") return ns_target[name];',
  455. init,
  456. "return ns[name];"
  457. ])},`,
  458. `${runtimeTemplate.method("has", "_, name", [
  459. "switch (name) {",
  460. Template.indent(
  461. [
  462. 'case "__esModule":',
  463. "case Symbol.toStringTag:",
  464. hasAsync
  465. ? `case ${RuntimeGlobals.deferredModuleAsyncTransitiveDependenciesSymbol}:`
  466. : "",
  467. Template.indent("return true;"),
  468. 'case "then":',
  469. Template.indent("return false;")
  470. ].filter(Boolean)
  471. ),
  472. "}",
  473. 'if (typeof name === "symbol") return name in ns_target;',
  474. init,
  475. "return name in ns;"
  476. ])},`,
  477. `${runtimeTemplate.method("ownKeys", "", [
  478. init,
  479. `${cst} filtered = Reflect.ownKeys(ns).filter(${runtimeTemplate.expressionFunction(
  480. 'x !== "then" && x !== Symbol.toStringTag',
  481. "x"
  482. )});`,
  483. `${cst} keys = ${
  484. runtimeTemplate.supportsSpread()
  485. ? "[...filtered, Symbol.toStringTag]"
  486. : "filtered.concat([Symbol.toStringTag])"
  487. };`,
  488. "return keys;"
  489. ])},`,
  490. `${runtimeTemplate.method("getOwnPropertyDescriptor", "_, name", [
  491. "switch (name) {",
  492. Template.indent([
  493. // Match the descriptors actually defined on `ns_target`
  494. // (non-configurable, non-writable, non-enumerable) so the
  495. // proxy invariant holds for both the trap result and any
  496. // post-init forwarding via the deleted-handler path.
  497. 'case "__esModule": return { value: true, writable: false, enumerable: false, configurable: false };',
  498. 'case Symbol.toStringTag: return { value: "Deferred Module", writable: false, enumerable: false, configurable: false };',
  499. 'case "then": return undefined;'
  500. ]),
  501. "}",
  502. 'if (typeof name === "symbol") return Reflect.getOwnPropertyDescriptor(ns_target, name);',
  503. init,
  504. `${lt} desc = Reflect.getOwnPropertyDescriptor(ns, name);`,
  505. 'if (mode & 2 && name == "default" && !desc) {',
  506. Template.indent("desc = { value: ns, configurable: true };"),
  507. "}",
  508. "return desc;"
  509. ])},`,
  510. // `defineProperty` always rejects, but per the TC39 spec it
  511. // must still trigger evaluation for string keys (the spec
  512. // algorithm calls `[[GetOwnProperty]]` first, which forces
  513. // evaluation on a deferred namespace). Symbol keys go through
  514. // OrdinaryDefineOwnProperty and do not trigger eval.
  515. `${runtimeTemplate.method("defineProperty", "_, name", [
  516. 'if (typeof name === "symbol" || name === "then") return false;',
  517. init,
  518. "return false;"
  519. ])},`,
  520. // `deleteProperty` rejects, but per the TC39 spec it must
  521. // still trigger evaluation for string keys (the spec
  522. // algorithm calls `GetModuleExportsList` for non-symbol-like
  523. // keys, forcing evaluation on a deferred namespace).
  524. `${runtimeTemplate.method("deleteProperty", "_, name", [
  525. 'if (typeof name === "symbol" || name === "then") return false;',
  526. init,
  527. "return false;"
  528. ])},`,
  529. // `set` always returns false without triggering evaluation —
  530. // the spec [[Set]] algorithm for Module Namespaces is just
  531. // "return false" (no [[GetOwnProperty]], no eval).
  532. `set: ${runtimeTemplate.returningFunction("false")},`
  533. ]),
  534. "}",
  535. // we don't fully emulate ES Module semantics in this Proxy to align with normal webpack esm namespace object.
  536. "return byMode[mode] = new Proxy(ns_target, handler);"
  537. ])};`;
  538. }
  539. }
  540. module.exports.MakeDeferredNamespaceObjectRuntimeModule =
  541. MakeDeferredNamespaceObjectRuntimeModule;
  542. module.exports.MakeOptimizedDeferredNamespaceObjectRuntimeModule =
  543. MakeOptimizedDeferredNamespaceObjectRuntimeModule;
  544. module.exports.getDeferredCycleModuleIds = getDeferredCycleModuleIds;
  545. module.exports.getDeferredCycleModules = getDeferredCycleModules;
  546. module.exports.getMakeDeferredNamespaceModeFromExportsType =
  547. getMakeDeferredNamespaceModeFromExportsType;
  548. module.exports.getOptimizedDeferredModule = getOptimizedDeferredModule;