mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-08-28 11:21:21 +00:00
341 lines
14 KiB
JavaScript
341 lines
14 KiB
JavaScript
/*
|
|
MIT License http://www.opensource.org/licenses/mit-license.php
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
const RuntimeGlobals = require("../RuntimeGlobals");
|
|
const Template = require("../Template");
|
|
const HelperRuntimeModule = require("./HelperRuntimeModule");
|
|
|
|
/** @typedef {import("../Module").RuntimeRequirements} RuntimeRequirements */
|
|
/** @typedef {import("../Module").ExportsType} ExportsType */
|
|
/** @typedef {import("../ChunkGraph").ModuleId} ModuleId */
|
|
|
|
/**
|
|
* @param {ExportsType} exportsType exports type
|
|
* @returns {string} mode
|
|
*/
|
|
function getMakeDeferredNamespaceModeFromExportsType(exportsType) {
|
|
// number is from createFakeNamespaceObject mode ^ 1
|
|
if (exportsType === "namespace") return `/* ${exportsType} */ 8`;
|
|
if (exportsType === "default-only") return `/* ${exportsType} */ 0`;
|
|
if (exportsType === "default-with-named") return `/* ${exportsType} */ 2`;
|
|
if (exportsType === "dynamic") return `/* ${exportsType} */ 6`;
|
|
throw new Error(`Unknown exports type: ${exportsType}`);
|
|
}
|
|
|
|
/**
|
|
* @param {string} moduleId moduleId
|
|
* @param {ExportsType} exportsType exportsType
|
|
* @param {(ModuleId | null)[]} asyncDepsIds asyncDepsIds
|
|
* @param {RuntimeRequirements} runtimeRequirements runtime requirements
|
|
* @returns {string} call make optimized deferred namespace object
|
|
*/
|
|
function getOptimizedDeferredModule(
|
|
moduleId,
|
|
exportsType,
|
|
asyncDepsIds,
|
|
runtimeRequirements
|
|
) {
|
|
runtimeRequirements.add(RuntimeGlobals.makeOptimizedDeferredNamespaceObject);
|
|
const mode = getMakeDeferredNamespaceModeFromExportsType(exportsType);
|
|
return `${RuntimeGlobals.makeOptimizedDeferredNamespaceObject}(${moduleId}, ${mode}${
|
|
asyncDepsIds.length > 0
|
|
? `, ${JSON.stringify(asyncDepsIds.filter((x) => x !== null))}`
|
|
: ""
|
|
})`;
|
|
}
|
|
|
|
class MakeOptimizedDeferredNamespaceObjectRuntimeModule extends HelperRuntimeModule {
|
|
/**
|
|
* @param {boolean} hasAsyncRuntime if async module is used.
|
|
*/
|
|
constructor(hasAsyncRuntime) {
|
|
super("make optimized deferred namespace object");
|
|
/** @type {boolean} */
|
|
this.hasAsyncRuntime = hasAsyncRuntime;
|
|
}
|
|
|
|
/**
|
|
* Generates runtime code for this runtime module.
|
|
* @returns {string | null} runtime code
|
|
*/
|
|
generate() {
|
|
if (!this.compilation) return null;
|
|
const fn = RuntimeGlobals.makeOptimizedDeferredNamespaceObject;
|
|
const hasAsync = this.hasAsyncRuntime;
|
|
return Template.asString([
|
|
// Note: must be a function (not arrow), because this is used in body!
|
|
`${fn} = function(moduleId, mode${hasAsync ? ", asyncDeps" : ""}) {`,
|
|
Template.indent([
|
|
"var r = this;",
|
|
hasAsync ? "var isAsync = asyncDeps && asyncDeps.length;" : "",
|
|
"var obj = {",
|
|
Template.indent([
|
|
"get a() {",
|
|
Template.indent([
|
|
"var exports = r(moduleId);",
|
|
hasAsync
|
|
? `if(isAsync) exports = exports[${RuntimeGlobals.asyncModuleExportSymbol}];`
|
|
: "",
|
|
// if exportsType is "namespace" we can generate the most optimized code,
|
|
// on the second access, we can avoid trigger the getter.
|
|
// we can also do this if exportsType is "dynamic" and there is a "__esModule" property on it.
|
|
'if(mode & 8 || (mode & 4 && exports.__esModule)) Object.defineProperty(this, "a", { value: exports });',
|
|
"return exports;"
|
|
]),
|
|
"}"
|
|
]),
|
|
"};",
|
|
hasAsync
|
|
? `if(isAsync) obj[${RuntimeGlobals.deferredModuleAsyncTransitiveDependenciesSymbol}] = asyncDeps;`
|
|
: "",
|
|
"return obj;"
|
|
]),
|
|
"};"
|
|
]);
|
|
}
|
|
}
|
|
|
|
class MakeDeferredNamespaceObjectRuntimeModule extends HelperRuntimeModule {
|
|
/**
|
|
* @param {boolean} hasAsyncRuntime if async module is used.
|
|
*/
|
|
constructor(hasAsyncRuntime) {
|
|
super("make deferred namespace object");
|
|
/** @type {boolean} */
|
|
this.hasAsyncRuntime = hasAsyncRuntime;
|
|
}
|
|
|
|
/**
|
|
* Generates runtime code for this runtime module.
|
|
* @returns {string | null} runtime code
|
|
*/
|
|
generate() {
|
|
if (!this.compilation) return null;
|
|
const { runtimeTemplate } = this.compilation;
|
|
const fn = RuntimeGlobals.makeDeferredNamespaceObject;
|
|
const hasAsync = this.hasAsyncRuntime;
|
|
const init = runtimeTemplate.supportsOptionalChaining()
|
|
? "init?.();"
|
|
: "if (init) init();";
|
|
return `${fn} = ${runtimeTemplate.basicFunction("moduleId, mode", [
|
|
// Per the TC39 import-defer spec, deferred namespaces are
|
|
// distinct from their eager counterparts and the same module
|
|
// referenced from multiple defer-import sites must yield the
|
|
// same object. Cache the Proxy / fake namespace per-moduleId so
|
|
// repeated calls (including across files) share identity.
|
|
//
|
|
// Bit 16 (`createFakeNamespaceObject`'s "return value when
|
|
// it's Promise-like" flag added by
|
|
// `RuntimeTemplate.moduleNamespacePromise` for dynamic
|
|
// imports) is irrelevant for deferred namespaces — the value
|
|
// passed into `createFakeNamespaceObject` here is always the
|
|
// resolved module exports (after unwrapping the async-module
|
|
// export symbol when present), never a Promise. Strip it
|
|
// once so all downstream behavior, the cache key, and the
|
|
// `createFakeNamespaceObject` call below see the same shape
|
|
// mode. This keeps static defer (mode 8) and dynamic
|
|
// `await import.defer` (mode 8 | 16) sharing the same
|
|
// Deferred Module Namespace object, while still keying by
|
|
// `(moduleId, mode)` so distinct exports-type shapes
|
|
// (e.g. one importer treats a CJS module as
|
|
// "default-with-named", another as "namespace") get
|
|
// distinct cache entries.
|
|
"mode &= ~16;",
|
|
"var byMode = __webpack_module_deferred_namespace_cache__[moduleId];",
|
|
"if (byMode && byMode[mode] !== undefined) return byMode[mode];",
|
|
"if (!byMode) byMode = __webpack_module_deferred_namespace_cache__[moduleId] = {};",
|
|
"var cachedModule = __webpack_module_cache__[moduleId];",
|
|
"if (cachedModule && cachedModule.error === undefined && !(mode & 8)) {",
|
|
Template.indent([
|
|
"var exports = cachedModule.exports;",
|
|
hasAsync
|
|
? `if (${RuntimeGlobals.asyncModuleExportSymbol} in exports) exports = exports[${RuntimeGlobals.asyncModuleExportSymbol}];`
|
|
: "",
|
|
`return byMode[mode] = ${RuntimeGlobals.createFakeNamespaceObject}(exports, mode);`
|
|
]),
|
|
"}",
|
|
"",
|
|
`var init = ${runtimeTemplate.basicFunction("", [
|
|
`ns = ${RuntimeGlobals.require}(moduleId);`,
|
|
hasAsync
|
|
? `if (${RuntimeGlobals.asyncModuleExportSymbol} in ns) ns = ns[${RuntimeGlobals.asyncModuleExportSymbol}];`
|
|
: "",
|
|
"init = null;",
|
|
"if (mode & 8 || mode & 4 && ns.__esModule && typeof ns === 'object') {",
|
|
Template.indent([
|
|
// Drop only the read-side traps after init: with the
|
|
// resolved namespace's own keys mirrored onto
|
|
// `ns_target` below, the default `Reflect` behavior
|
|
// returns the right values via the live-binding
|
|
// getters, so we no longer need to intercept `get` /
|
|
// `has` / `ownKeys` / `getOwnPropertyDescriptor`.
|
|
//
|
|
// The mutation traps (`set`, `deleteProperty`,
|
|
// `defineProperty`) are kept because per the TC39
|
|
// import-defer spec, `[[Set]]` / `[[Delete]]` /
|
|
// `[[DefineOwnProperty]]` on a Deferred Module
|
|
// Namespace Exotic Object never succeed — and the
|
|
// proxy target itself remains extensible
|
|
// (architecturally we cannot freeze it up-front),
|
|
// so without these traps `ns.notExported = "x"`
|
|
// after evaluation would silently create a property
|
|
// on the target instead of returning false.
|
|
"delete handler.get;",
|
|
"delete handler.has;",
|
|
"delete handler.ownKeys;",
|
|
"delete handler.getOwnPropertyDescriptor;"
|
|
]),
|
|
"} else {",
|
|
Template.indent([
|
|
`ns = ${RuntimeGlobals.createFakeNamespaceObject}(ns, mode);`
|
|
]),
|
|
"}",
|
|
// Mirror own properties from the resolved namespace onto the proxy
|
|
// target so that proxy invariants hold for callers that structurally
|
|
// introspect via `Object.keys` / `Object.getOwnPropertyNames` /
|
|
// `Object.getOwnPropertyDescriptor`: when our trap reports a
|
|
// non-configurable descriptor for a key, the target must also have
|
|
// that key with a matching descriptor.
|
|
//
|
|
// `__esModule` and `Symbol.toStringTag` are intentionally skipped:
|
|
// the proxy synthesizes "Deferred Module" / true regardless of what
|
|
// the underlying namespace exposes (per the TC39 import-defer
|
|
// proposal, the [[StringTag]] of a Deferred Module Namespace
|
|
// Exotic Object is "Deferred Module"), and the target was already
|
|
// pre-populated with those values below.
|
|
"var keys = Reflect.ownKeys(ns);",
|
|
"for (var i = 0; i < keys.length; i++) {",
|
|
Template.indent([
|
|
"var k = keys[i];",
|
|
'if (k === "__esModule" || k === Symbol.toStringTag) continue;',
|
|
"if (!Object.prototype.hasOwnProperty.call(ns_target, k)) {",
|
|
Template.indent([
|
|
"try { Object.defineProperty(ns_target, k, Reflect.getOwnPropertyDescriptor(ns, k)); } catch (_) {}"
|
|
]),
|
|
"}"
|
|
]),
|
|
"}"
|
|
])};`,
|
|
"",
|
|
// The proxy target is a fresh placeholder, separate from
|
|
// `__webpack_module_deferred_exports__[moduleId]` (which is reused
|
|
// by `__webpack_require__` as `module.exports` for deferred-loaded
|
|
// modules and would conflict with our pre-populated synthetic
|
|
// `__esModule` / `Symbol.toStringTag` non-configurable properties).
|
|
// Using a dedicated target keeps the proxy invariant-compliant
|
|
// without interfering with the module's own exports object.
|
|
"var ns_target = { __proto__: null };",
|
|
// Pre-populate the synthetic deferred-namespace properties with
|
|
// fully non-configurable, non-writable, non-enumerable descriptors
|
|
// (matching the TC39 import-defer spec for Module Namespace
|
|
// Exotic Objects). The trap returns the same descriptors below.
|
|
'Object.defineProperty(ns_target, "__esModule", { value: true });',
|
|
'Object.defineProperty(ns_target, Symbol.toStringTag, { value: "Deferred Module" });',
|
|
"var ns = ns_target;",
|
|
"var handler = {",
|
|
Template.indent([
|
|
"__proto__: null,",
|
|
// Per the TC39 import-defer proposal, `IsSymbolLikeNamespaceKey`
|
|
// returns true for any Symbol-keyed access (and for "then"); such
|
|
// accesses go through `OrdinaryGetOwnProperty` and must not
|
|
// trigger evaluation of the deferred module. The Symbol checks
|
|
// below short-circuit to the pre-populated target without
|
|
// running `init()`.
|
|
`get: ${runtimeTemplate.basicFunction("_, name", [
|
|
"switch (name) {",
|
|
Template.indent([
|
|
'case "__esModule": return true;',
|
|
'case Symbol.toStringTag: return "Deferred Module";',
|
|
'case "then": return undefined;'
|
|
]),
|
|
"}",
|
|
'if (typeof name === "symbol") return ns_target[name];',
|
|
init,
|
|
"return ns[name];"
|
|
])},`,
|
|
`has: ${runtimeTemplate.basicFunction("_, name", [
|
|
"switch (name) {",
|
|
Template.indent(
|
|
[
|
|
'case "__esModule":',
|
|
"case Symbol.toStringTag:",
|
|
hasAsync
|
|
? `case ${RuntimeGlobals.deferredModuleAsyncTransitiveDependenciesSymbol}:`
|
|
: "",
|
|
Template.indent("return true;"),
|
|
'case "then":',
|
|
Template.indent("return false;")
|
|
].filter(Boolean)
|
|
),
|
|
"}",
|
|
'if (typeof name === "symbol") return name in ns_target;',
|
|
init,
|
|
"return name in ns;"
|
|
])},`,
|
|
`ownKeys: ${runtimeTemplate.basicFunction("", [
|
|
init,
|
|
`var keys = Reflect.ownKeys(ns).filter(${runtimeTemplate.expressionFunction('x !== "then" && x !== Symbol.toStringTag', "x")}).concat([Symbol.toStringTag]);`,
|
|
"return keys;"
|
|
])},`,
|
|
`getOwnPropertyDescriptor: ${runtimeTemplate.basicFunction("_, name", [
|
|
"switch (name) {",
|
|
Template.indent([
|
|
// Match the descriptors actually defined on `ns_target`
|
|
// (non-configurable, non-writable, non-enumerable) so the
|
|
// proxy invariant holds for both the trap result and any
|
|
// post-init forwarding via the deleted-handler path.
|
|
'case "__esModule": return { value: true, writable: false, enumerable: false, configurable: false };',
|
|
'case Symbol.toStringTag: return { value: "Deferred Module", writable: false, enumerable: false, configurable: false };',
|
|
'case "then": return undefined;'
|
|
]),
|
|
"}",
|
|
'if (typeof name === "symbol") return Reflect.getOwnPropertyDescriptor(ns_target, name);',
|
|
init,
|
|
"var desc = Reflect.getOwnPropertyDescriptor(ns, name);",
|
|
'if (mode & 2 && name == "default" && !desc) {',
|
|
Template.indent("desc = { value: ns, configurable: true };"),
|
|
"}",
|
|
"return desc;"
|
|
])},`,
|
|
// `defineProperty` always rejects, but per the TC39 spec it
|
|
// must still trigger evaluation for string keys (the spec
|
|
// algorithm calls `[[GetOwnProperty]]` first, which forces
|
|
// evaluation on a deferred namespace). Symbol keys go through
|
|
// OrdinaryDefineOwnProperty and do not trigger eval.
|
|
`defineProperty: ${runtimeTemplate.basicFunction("_, name", [
|
|
'if (typeof name === "symbol" || name === "then") return false;',
|
|
init,
|
|
"return false;"
|
|
])},`,
|
|
// `deleteProperty` rejects, but per the TC39 spec it must
|
|
// still trigger evaluation for string keys (the spec
|
|
// algorithm calls `GetModuleExportsList` for non-symbol-like
|
|
// keys, forcing evaluation on a deferred namespace).
|
|
`deleteProperty: ${runtimeTemplate.basicFunction("_, name", [
|
|
'if (typeof name === "symbol" || name === "then") return false;',
|
|
init,
|
|
"return false;"
|
|
])},`,
|
|
// `set` always returns false without triggering evaluation —
|
|
// the spec [[Set]] algorithm for Module Namespaces is just
|
|
// "return false" (no [[GetOwnProperty]], no eval).
|
|
`set: ${runtimeTemplate.returningFunction("false")},`
|
|
]),
|
|
"}",
|
|
// we don't fully emulate ES Module semantics in this Proxy to align with normal webpack esm namespace object.
|
|
"return byMode[mode] = new Proxy(ns_target, handler);"
|
|
])};`;
|
|
}
|
|
}
|
|
|
|
module.exports.MakeDeferredNamespaceObjectRuntimeModule =
|
|
MakeDeferredNamespaceObjectRuntimeModule;
|
|
module.exports.MakeOptimizedDeferredNamespaceObjectRuntimeModule =
|
|
MakeOptimizedDeferredNamespaceObjectRuntimeModule;
|
|
module.exports.getMakeDeferredNamespaceModeFromExportsType =
|
|
getMakeDeferredNamespaceModeFromExportsType;
|
|
module.exports.getOptimizedDeferredModule = getOptimizedDeferredModule;
|