| [9af201e] | 1 | /*
|
|---|
| 2 | MIT License http://www.opensource.org/licenses/mit-license.php
|
|---|
| 3 | */
|
|---|
| 4 |
|
|---|
| 5 | "use strict";
|
|---|
| 6 |
|
|---|
| 7 | const RuntimeGlobals = require("../RuntimeGlobals");
|
|---|
| 8 | const Template = require("../Template");
|
|---|
| 9 | const HelperRuntimeModule = require("./HelperRuntimeModule");
|
|---|
| 10 |
|
|---|
| 11 | /** @typedef {import("../Module").RuntimeRequirements} RuntimeRequirements */
|
|---|
| 12 | /** @typedef {import("../Module").ExportsType} ExportsType */
|
|---|
| 13 | /** @typedef {import("../ChunkGraph").ModuleId} ModuleId */
|
|---|
| 14 |
|
|---|
| 15 | /**
|
|---|
| 16 | * @param {ExportsType} exportsType exports type
|
|---|
| 17 | * @returns {string} mode
|
|---|
| 18 | */
|
|---|
| 19 | function getMakeDeferredNamespaceModeFromExportsType(exportsType) {
|
|---|
| 20 | // number is from createFakeNamespaceObject mode ^ 1
|
|---|
| 21 | if (exportsType === "namespace") return `/* ${exportsType} */ 8`;
|
|---|
| 22 | if (exportsType === "default-only") return `/* ${exportsType} */ 0`;
|
|---|
| 23 | if (exportsType === "default-with-named") return `/* ${exportsType} */ 2`;
|
|---|
| 24 | if (exportsType === "dynamic") return `/* ${exportsType} */ 6`;
|
|---|
| 25 | throw new Error(`Unknown exports type: ${exportsType}`);
|
|---|
| 26 | }
|
|---|
| 27 |
|
|---|
| 28 | /**
|
|---|
| 29 | * @param {string} moduleId moduleId
|
|---|
| 30 | * @param {ExportsType} exportsType exportsType
|
|---|
| 31 | * @param {(ModuleId | null)[]} asyncDepsIds asyncDepsIds
|
|---|
| 32 | * @param {RuntimeRequirements} runtimeRequirements runtime requirements
|
|---|
| 33 | * @returns {string} call make optimized deferred namespace object
|
|---|
| 34 | */
|
|---|
| 35 | function getOptimizedDeferredModule(
|
|---|
| 36 | moduleId,
|
|---|
| 37 | exportsType,
|
|---|
| 38 | asyncDepsIds,
|
|---|
| 39 | runtimeRequirements
|
|---|
| 40 | ) {
|
|---|
| 41 | runtimeRequirements.add(RuntimeGlobals.makeOptimizedDeferredNamespaceObject);
|
|---|
| 42 | const mode = getMakeDeferredNamespaceModeFromExportsType(exportsType);
|
|---|
| 43 | return `${RuntimeGlobals.makeOptimizedDeferredNamespaceObject}(${moduleId}, ${mode}${
|
|---|
| 44 | asyncDepsIds.length > 0
|
|---|
| 45 | ? `, ${JSON.stringify(asyncDepsIds.filter((x) => x !== null))}`
|
|---|
| 46 | : ""
|
|---|
| 47 | })`;
|
|---|
| 48 | }
|
|---|
| 49 |
|
|---|
| 50 | class MakeOptimizedDeferredNamespaceObjectRuntimeModule extends HelperRuntimeModule {
|
|---|
| 51 | /**
|
|---|
| 52 | * @param {boolean} hasAsyncRuntime if async module is used.
|
|---|
| 53 | */
|
|---|
| 54 | constructor(hasAsyncRuntime) {
|
|---|
| 55 | super("make optimized deferred namespace object");
|
|---|
| 56 | /** @type {boolean} */
|
|---|
| 57 | this.hasAsyncRuntime = hasAsyncRuntime;
|
|---|
| 58 | }
|
|---|
| 59 |
|
|---|
| 60 | /**
|
|---|
| 61 | * Generates runtime code for this runtime module.
|
|---|
| 62 | * @returns {string | null} runtime code
|
|---|
| 63 | */
|
|---|
| 64 | generate() {
|
|---|
| 65 | if (!this.compilation) return null;
|
|---|
| 66 | const fn = RuntimeGlobals.makeOptimizedDeferredNamespaceObject;
|
|---|
| 67 | const hasAsync = this.hasAsyncRuntime;
|
|---|
| 68 | return Template.asString([
|
|---|
| 69 | // Note: must be a function (not arrow), because this is used in body!
|
|---|
| 70 | `${fn} = function(moduleId, mode${hasAsync ? ", asyncDeps" : ""}) {`,
|
|---|
| 71 | Template.indent([
|
|---|
| 72 | "var r = this;",
|
|---|
| 73 | hasAsync ? "var isAsync = asyncDeps && asyncDeps.length;" : "",
|
|---|
| 74 | "var obj = {",
|
|---|
| 75 | Template.indent([
|
|---|
| 76 | "get a() {",
|
|---|
| 77 | Template.indent([
|
|---|
| 78 | "var exports = r(moduleId);",
|
|---|
| 79 | hasAsync
|
|---|
| 80 | ? `if(isAsync) exports = exports[${RuntimeGlobals.asyncModuleExportSymbol}];`
|
|---|
| 81 | : "",
|
|---|
| 82 | // if exportsType is "namespace" we can generate the most optimized code,
|
|---|
| 83 | // on the second access, we can avoid trigger the getter.
|
|---|
| 84 | // we can also do this if exportsType is "dynamic" and there is a "__esModule" property on it.
|
|---|
| 85 | 'if(mode & 8 || (mode & 4 && exports.__esModule)) Object.defineProperty(this, "a", { value: exports });',
|
|---|
| 86 | "return exports;"
|
|---|
| 87 | ]),
|
|---|
| 88 | "}"
|
|---|
| 89 | ]),
|
|---|
| 90 | "};",
|
|---|
| 91 | hasAsync
|
|---|
| 92 | ? `if(isAsync) obj[${RuntimeGlobals.deferredModuleAsyncTransitiveDependenciesSymbol}] = asyncDeps;`
|
|---|
| 93 | : "",
|
|---|
| 94 | "return obj;"
|
|---|
| 95 | ]),
|
|---|
| 96 | "};"
|
|---|
| 97 | ]);
|
|---|
| 98 | }
|
|---|
| 99 | }
|
|---|
| 100 |
|
|---|
| 101 | class MakeDeferredNamespaceObjectRuntimeModule extends HelperRuntimeModule {
|
|---|
| 102 | /**
|
|---|
| 103 | * @param {boolean} hasAsyncRuntime if async module is used.
|
|---|
| 104 | */
|
|---|
| 105 | constructor(hasAsyncRuntime) {
|
|---|
| 106 | super("make deferred namespace object");
|
|---|
| 107 | /** @type {boolean} */
|
|---|
| 108 | this.hasAsyncRuntime = hasAsyncRuntime;
|
|---|
| 109 | }
|
|---|
| 110 |
|
|---|
| 111 | /**
|
|---|
| 112 | * Generates runtime code for this runtime module.
|
|---|
| 113 | * @returns {string | null} runtime code
|
|---|
| 114 | */
|
|---|
| 115 | generate() {
|
|---|
| 116 | if (!this.compilation) return null;
|
|---|
| 117 | const { runtimeTemplate } = this.compilation;
|
|---|
| 118 | const fn = RuntimeGlobals.makeDeferredNamespaceObject;
|
|---|
| 119 | const hasAsync = this.hasAsyncRuntime;
|
|---|
| 120 | const init = runtimeTemplate.supportsOptionalChaining()
|
|---|
| 121 | ? "init?.();"
|
|---|
| 122 | : "if (init) init();";
|
|---|
| 123 | return `${fn} = ${runtimeTemplate.basicFunction("moduleId, mode", [
|
|---|
| 124 | // Per the TC39 import-defer spec, deferred namespaces are
|
|---|
| 125 | // distinct from their eager counterparts and the same module
|
|---|
| 126 | // referenced from multiple defer-import sites must yield the
|
|---|
| 127 | // same object. Cache the Proxy / fake namespace per-moduleId so
|
|---|
| 128 | // repeated calls (including across files) share identity.
|
|---|
| 129 | //
|
|---|
| 130 | // Bit 16 (`createFakeNamespaceObject`'s "return value when
|
|---|
| 131 | // it's Promise-like" flag added by
|
|---|
| 132 | // `RuntimeTemplate.moduleNamespacePromise` for dynamic
|
|---|
| 133 | // imports) is irrelevant for deferred namespaces — the value
|
|---|
| 134 | // passed into `createFakeNamespaceObject` here is always the
|
|---|
| 135 | // resolved module exports (after unwrapping the async-module
|
|---|
| 136 | // export symbol when present), never a Promise. Strip it
|
|---|
| 137 | // once so all downstream behavior, the cache key, and the
|
|---|
| 138 | // `createFakeNamespaceObject` call below see the same shape
|
|---|
| 139 | // mode. This keeps static defer (mode 8) and dynamic
|
|---|
| 140 | // `await import.defer` (mode 8 | 16) sharing the same
|
|---|
| 141 | // Deferred Module Namespace object, while still keying by
|
|---|
| 142 | // `(moduleId, mode)` so distinct exports-type shapes
|
|---|
| 143 | // (e.g. one importer treats a CJS module as
|
|---|
| 144 | // "default-with-named", another as "namespace") get
|
|---|
| 145 | // distinct cache entries.
|
|---|
| 146 | "mode &= ~16;",
|
|---|
| 147 | "var byMode = __webpack_module_deferred_namespace_cache__[moduleId];",
|
|---|
| 148 | "if (byMode && byMode[mode] !== undefined) return byMode[mode];",
|
|---|
| 149 | "if (!byMode) byMode = __webpack_module_deferred_namespace_cache__[moduleId] = {};",
|
|---|
| 150 | "var cachedModule = __webpack_module_cache__[moduleId];",
|
|---|
| 151 | "if (cachedModule && cachedModule.error === undefined && !(mode & 8)) {",
|
|---|
| 152 | Template.indent([
|
|---|
| 153 | "var exports = cachedModule.exports;",
|
|---|
| 154 | hasAsync
|
|---|
| 155 | ? `if (${RuntimeGlobals.asyncModuleExportSymbol} in exports) exports = exports[${RuntimeGlobals.asyncModuleExportSymbol}];`
|
|---|
| 156 | : "",
|
|---|
| 157 | `return byMode[mode] = ${RuntimeGlobals.createFakeNamespaceObject}(exports, mode);`
|
|---|
| 158 | ]),
|
|---|
| 159 | "}",
|
|---|
| 160 | "",
|
|---|
| 161 | `var init = ${runtimeTemplate.basicFunction("", [
|
|---|
| 162 | `ns = ${RuntimeGlobals.require}(moduleId);`,
|
|---|
| 163 | hasAsync
|
|---|
| 164 | ? `if (${RuntimeGlobals.asyncModuleExportSymbol} in ns) ns = ns[${RuntimeGlobals.asyncModuleExportSymbol}];`
|
|---|
| 165 | : "",
|
|---|
| 166 | "init = null;",
|
|---|
| 167 | "if (mode & 8 || mode & 4 && ns.__esModule && typeof ns === 'object') {",
|
|---|
| 168 | Template.indent([
|
|---|
| 169 | // Drop only the read-side traps after init: with the
|
|---|
| 170 | // resolved namespace's own keys mirrored onto
|
|---|
| 171 | // `ns_target` below, the default `Reflect` behavior
|
|---|
| 172 | // returns the right values via the live-binding
|
|---|
| 173 | // getters, so we no longer need to intercept `get` /
|
|---|
| 174 | // `has` / `ownKeys` / `getOwnPropertyDescriptor`.
|
|---|
| 175 | //
|
|---|
| 176 | // The mutation traps (`set`, `deleteProperty`,
|
|---|
| 177 | // `defineProperty`) are kept because per the TC39
|
|---|
| 178 | // import-defer spec, `[[Set]]` / `[[Delete]]` /
|
|---|
| 179 | // `[[DefineOwnProperty]]` on a Deferred Module
|
|---|
| 180 | // Namespace Exotic Object never succeed — and the
|
|---|
| 181 | // proxy target itself remains extensible
|
|---|
| 182 | // (architecturally we cannot freeze it up-front),
|
|---|
| 183 | // so without these traps `ns.notExported = "x"`
|
|---|
| 184 | // after evaluation would silently create a property
|
|---|
| 185 | // on the target instead of returning false.
|
|---|
| 186 | "delete handler.get;",
|
|---|
| 187 | "delete handler.has;",
|
|---|
| 188 | "delete handler.ownKeys;",
|
|---|
| 189 | "delete handler.getOwnPropertyDescriptor;"
|
|---|
| 190 | ]),
|
|---|
| 191 | "} else {",
|
|---|
| 192 | Template.indent([
|
|---|
| 193 | `ns = ${RuntimeGlobals.createFakeNamespaceObject}(ns, mode);`
|
|---|
| 194 | ]),
|
|---|
| 195 | "}",
|
|---|
| 196 | // Mirror own properties from the resolved namespace onto the proxy
|
|---|
| 197 | // target so that proxy invariants hold for callers that structurally
|
|---|
| 198 | // introspect via `Object.keys` / `Object.getOwnPropertyNames` /
|
|---|
| 199 | // `Object.getOwnPropertyDescriptor`: when our trap reports a
|
|---|
| 200 | // non-configurable descriptor for a key, the target must also have
|
|---|
| 201 | // that key with a matching descriptor.
|
|---|
| 202 | //
|
|---|
| 203 | // `__esModule` and `Symbol.toStringTag` are intentionally skipped:
|
|---|
| 204 | // the proxy synthesizes "Deferred Module" / true regardless of what
|
|---|
| 205 | // the underlying namespace exposes (per the TC39 import-defer
|
|---|
| 206 | // proposal, the [[StringTag]] of a Deferred Module Namespace
|
|---|
| 207 | // Exotic Object is "Deferred Module"), and the target was already
|
|---|
| 208 | // pre-populated with those values below.
|
|---|
| 209 | "var keys = Reflect.ownKeys(ns);",
|
|---|
| 210 | "for (var i = 0; i < keys.length; i++) {",
|
|---|
| 211 | Template.indent([
|
|---|
| 212 | "var k = keys[i];",
|
|---|
| 213 | 'if (k === "__esModule" || k === Symbol.toStringTag) continue;',
|
|---|
| 214 | "if (!Object.prototype.hasOwnProperty.call(ns_target, k)) {",
|
|---|
| 215 | Template.indent([
|
|---|
| 216 | "try { Object.defineProperty(ns_target, k, Reflect.getOwnPropertyDescriptor(ns, k)); } catch (_) {}"
|
|---|
| 217 | ]),
|
|---|
| 218 | "}"
|
|---|
| 219 | ]),
|
|---|
| 220 | "}"
|
|---|
| 221 | ])};`,
|
|---|
| 222 | "",
|
|---|
| 223 | // The proxy target is a fresh placeholder, separate from
|
|---|
| 224 | // `__webpack_module_deferred_exports__[moduleId]` (which is reused
|
|---|
| 225 | // by `__webpack_require__` as `module.exports` for deferred-loaded
|
|---|
| 226 | // modules and would conflict with our pre-populated synthetic
|
|---|
| 227 | // `__esModule` / `Symbol.toStringTag` non-configurable properties).
|
|---|
| 228 | // Using a dedicated target keeps the proxy invariant-compliant
|
|---|
| 229 | // without interfering with the module's own exports object.
|
|---|
| 230 | "var ns_target = { __proto__: null };",
|
|---|
| 231 | // Pre-populate the synthetic deferred-namespace properties with
|
|---|
| 232 | // fully non-configurable, non-writable, non-enumerable descriptors
|
|---|
| 233 | // (matching the TC39 import-defer spec for Module Namespace
|
|---|
| 234 | // Exotic Objects). The trap returns the same descriptors below.
|
|---|
| 235 | 'Object.defineProperty(ns_target, "__esModule", { value: true });',
|
|---|
| 236 | 'Object.defineProperty(ns_target, Symbol.toStringTag, { value: "Deferred Module" });',
|
|---|
| 237 | "var ns = ns_target;",
|
|---|
| 238 | "var handler = {",
|
|---|
| 239 | Template.indent([
|
|---|
| 240 | "__proto__: null,",
|
|---|
| 241 | // Per the TC39 import-defer proposal, `IsSymbolLikeNamespaceKey`
|
|---|
| 242 | // returns true for any Symbol-keyed access (and for "then"); such
|
|---|
| 243 | // accesses go through `OrdinaryGetOwnProperty` and must not
|
|---|
| 244 | // trigger evaluation of the deferred module. The Symbol checks
|
|---|
| 245 | // below short-circuit to the pre-populated target without
|
|---|
| 246 | // running `init()`.
|
|---|
| 247 | `get: ${runtimeTemplate.basicFunction("_, name", [
|
|---|
| 248 | "switch (name) {",
|
|---|
| 249 | Template.indent([
|
|---|
| 250 | 'case "__esModule": return true;',
|
|---|
| 251 | 'case Symbol.toStringTag: return "Deferred Module";',
|
|---|
| 252 | 'case "then": return undefined;'
|
|---|
| 253 | ]),
|
|---|
| 254 | "}",
|
|---|
| 255 | 'if (typeof name === "symbol") return ns_target[name];',
|
|---|
| 256 | init,
|
|---|
| 257 | "return ns[name];"
|
|---|
| 258 | ])},`,
|
|---|
| 259 | `has: ${runtimeTemplate.basicFunction("_, name", [
|
|---|
| 260 | "switch (name) {",
|
|---|
| 261 | Template.indent(
|
|---|
| 262 | [
|
|---|
| 263 | 'case "__esModule":',
|
|---|
| 264 | "case Symbol.toStringTag:",
|
|---|
| 265 | hasAsync
|
|---|
| 266 | ? `case ${RuntimeGlobals.deferredModuleAsyncTransitiveDependenciesSymbol}:`
|
|---|
| 267 | : "",
|
|---|
| 268 | Template.indent("return true;"),
|
|---|
| 269 | 'case "then":',
|
|---|
| 270 | Template.indent("return false;")
|
|---|
| 271 | ].filter(Boolean)
|
|---|
| 272 | ),
|
|---|
| 273 | "}",
|
|---|
| 274 | 'if (typeof name === "symbol") return name in ns_target;',
|
|---|
| 275 | init,
|
|---|
| 276 | "return name in ns;"
|
|---|
| 277 | ])},`,
|
|---|
| 278 | `ownKeys: ${runtimeTemplate.basicFunction("", [
|
|---|
| 279 | init,
|
|---|
| 280 | `var keys = Reflect.ownKeys(ns).filter(${runtimeTemplate.expressionFunction('x !== "then" && x !== Symbol.toStringTag', "x")}).concat([Symbol.toStringTag]);`,
|
|---|
| 281 | "return keys;"
|
|---|
| 282 | ])},`,
|
|---|
| 283 | `getOwnPropertyDescriptor: ${runtimeTemplate.basicFunction("_, name", [
|
|---|
| 284 | "switch (name) {",
|
|---|
| 285 | Template.indent([
|
|---|
| 286 | // Match the descriptors actually defined on `ns_target`
|
|---|
| 287 | // (non-configurable, non-writable, non-enumerable) so the
|
|---|
| 288 | // proxy invariant holds for both the trap result and any
|
|---|
| 289 | // post-init forwarding via the deleted-handler path.
|
|---|
| 290 | 'case "__esModule": return { value: true, writable: false, enumerable: false, configurable: false };',
|
|---|
| 291 | 'case Symbol.toStringTag: return { value: "Deferred Module", writable: false, enumerable: false, configurable: false };',
|
|---|
| 292 | 'case "then": return undefined;'
|
|---|
| 293 | ]),
|
|---|
| 294 | "}",
|
|---|
| 295 | 'if (typeof name === "symbol") return Reflect.getOwnPropertyDescriptor(ns_target, name);',
|
|---|
| 296 | init,
|
|---|
| 297 | "var desc = Reflect.getOwnPropertyDescriptor(ns, name);",
|
|---|
| 298 | 'if (mode & 2 && name == "default" && !desc) {',
|
|---|
| 299 | Template.indent("desc = { value: ns, configurable: true };"),
|
|---|
| 300 | "}",
|
|---|
| 301 | "return desc;"
|
|---|
| 302 | ])},`,
|
|---|
| 303 | // `defineProperty` always rejects, but per the TC39 spec it
|
|---|
| 304 | // must still trigger evaluation for string keys (the spec
|
|---|
| 305 | // algorithm calls `[[GetOwnProperty]]` first, which forces
|
|---|
| 306 | // evaluation on a deferred namespace). Symbol keys go through
|
|---|
| 307 | // OrdinaryDefineOwnProperty and do not trigger eval.
|
|---|
| 308 | `defineProperty: ${runtimeTemplate.basicFunction("_, name", [
|
|---|
| 309 | 'if (typeof name === "symbol" || name === "then") return false;',
|
|---|
| 310 | init,
|
|---|
| 311 | "return false;"
|
|---|
| 312 | ])},`,
|
|---|
| 313 | // `deleteProperty` rejects, but per the TC39 spec it must
|
|---|
| 314 | // still trigger evaluation for string keys (the spec
|
|---|
| 315 | // algorithm calls `GetModuleExportsList` for non-symbol-like
|
|---|
| 316 | // keys, forcing evaluation on a deferred namespace).
|
|---|
| 317 | `deleteProperty: ${runtimeTemplate.basicFunction("_, name", [
|
|---|
| 318 | 'if (typeof name === "symbol" || name === "then") return false;',
|
|---|
| 319 | init,
|
|---|
| 320 | "return false;"
|
|---|
| 321 | ])},`,
|
|---|
| 322 | // `set` always returns false without triggering evaluation —
|
|---|
| 323 | // the spec [[Set]] algorithm for Module Namespaces is just
|
|---|
| 324 | // "return false" (no [[GetOwnProperty]], no eval).
|
|---|
| 325 | `set: ${runtimeTemplate.returningFunction("false")},`
|
|---|
| 326 | ]),
|
|---|
| 327 | "}",
|
|---|
| 328 | // we don't fully emulate ES Module semantics in this Proxy to align with normal webpack esm namespace object.
|
|---|
| 329 | "return byMode[mode] = new Proxy(ns_target, handler);"
|
|---|
| 330 | ])};`;
|
|---|
| 331 | }
|
|---|
| 332 | }
|
|---|
| 333 |
|
|---|
| 334 | module.exports.MakeDeferredNamespaceObjectRuntimeModule =
|
|---|
| 335 | MakeDeferredNamespaceObjectRuntimeModule;
|
|---|
| 336 | module.exports.MakeOptimizedDeferredNamespaceObjectRuntimeModule =
|
|---|
| 337 | MakeOptimizedDeferredNamespaceObjectRuntimeModule;
|
|---|
| 338 | module.exports.getMakeDeferredNamespaceModeFromExportsType =
|
|---|
| 339 | getMakeDeferredNamespaceModeFromExportsType;
|
|---|
| 340 | module.exports.getOptimizedDeferredModule = getOptimizedDeferredModule;
|
|---|