/
githubmirror
/
webpack
Обзор
Документация
Войти
/
githubmirror
/
webpack
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
lib/dependencies/HarmonyExportExpressionDependency.js
333 строки
11 KB
hai-x
feat: wrap concatenated ESM/CJS and inline CJS require edges (#21519)
03 авг 2026, 14:58
Не верифицирован
03 авг 2026, 14:58
a6d13aa
Код
Авторство
О чём код?
/* MIT License http://www.opensource.org/licenses/mit-license.php Author Tobias Koppers @sokra */ "use strict"; const ConcatenationScope = require("../ConcatenationScope"); const Dependency = require("../Dependency"); const InitFragment = require("../InitFragment"); const RuntimeGlobals = require("../RuntimeGlobals"); const { InlinedUsedName } = require("../optimize/InlineExports"); const makeSerializable = require("../util/makeSerializable"); const { propertyAccess } = require("../util/property"); const ExportBindingInitFragment = require("./ExportBindingInitFragment"); const HarmonyExportInitFragment = require("./HarmonyExportInitFragment"); const NullDependency = require("./NullDependency"); const DEFAULT_EXPORT_NAME = "default"; /** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */ /** @typedef {import("../Dependency").ExportsSpec} ExportsSpec */ /** @typedef {import("../Dependency").LazyUntil} LazyUntil */ /** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */ /** @typedef {import("../ModuleGraph")} ModuleGraph */ /** @typedef {import("../ModuleGraphConnection").ConnectionState} ConnectionState */ /** @typedef {import("../optimize/InlineExports").InlinedValue} InlinedValue */ /** @typedef {import("../javascript/JavascriptParser").Range} Range */ /** @typedef {string | { id?: string | undefined, range: Range, prefix: string, suffix: string }} DeclarationId */ /** @typedef {import("../serialization/ObjectMiddleware").ObjectDeserializerContext<[Range, Range, string, DeclarationId | undefined, boolean]>} ObjectDeserializerContext */ /** @typedef {import("../serialization/ObjectMiddleware").ObjectSerializerContext<[Range, Range, string, DeclarationId | undefined, boolean]>} ObjectSerializerContext */ /** @typedef {import("./HarmonyExportInitFragment").ExportMap} ExportMap */ /** @typedef {import("../javascript/JavascriptModule").JavascriptModuleBuildInfo} JavascriptModuleBuildInfo */ class HarmonyExportExpressionDependency extends NullDependency { /** * Creates an instance of HarmonyExportExpressionDependency. * @param {Range} range range * @param {Range} rangeStatement range statement * @param {string} prefix prefix * @param {string | { id?: string | undefined, range: Range, prefix: string, suffix: string }=} declarationId declaration id * @param {InlinedValue=} constValue inlined primitive when `export default <const>` is inline-eligible */ constructor(range, rangeStatement, prefix, declarationId, constValue) { super(); this.range = range; this.rangeStatement = rangeStatement; /** @type {string} */ this.prefix = prefix; this.declarationId = declarationId; this.constValue = constValue; /** @type {boolean} */ this.isAnonymousDefault = false; } get type() { return "harmony export expression"; } /** * Returns how this dependency may be deferred when its parent module is side-effect-free (lazy barrel optimization). * @returns {LazyUntil | null} lazy classification, null when it must be processed eagerly */ getLazyUntil() { return Dependency.LAZY_UNTIL_LOCAL; } /** * Returns the export name for a `LAZY_UNTIL_LOCAL`/`LAZY_UNTIL_ID` classification (lazy barrel optimization). * @returns {string | null} export name, null when not applicable */ getLazyName() { return DEFAULT_EXPORT_NAME; } /** * Returns the exported names * @param {ModuleGraph} moduleGraph module graph * @returns {ExportsSpec | undefined} export names */ getExports(moduleGraph) { const module = moduleGraph.getParentModule(this); const pureFunctions = module && module.buildInfo && /** @type {JavascriptModuleBuildInfo} */ (module.buildInfo).pureFunctions; const isPure = Boolean( pureFunctions && pureFunctions.has(DEFAULT_EXPORT_NAME) ); return { exports: [ { name: DEFAULT_EXPORT_NAME, isPure: isPure || undefined, inlined: this.constValue } ], priority: 1, terminalBinding: true, dependencies: undefined }; } /** * Gets module evaluation side effects state. * @param {ModuleGraph} moduleGraph the module graph * @returns {ConnectionState} how this dependency connects the module to referencing modules */ getModuleEvaluationSideEffectsState(moduleGraph) { // The expression/declaration is already covered by SideEffectsFlagPlugin return false; } /** * Serializes this instance into the provided serializer context. * @param {ObjectSerializerContext} context context */ serialize(context) { context .write(this.range) .write(this.rangeStatement) .write(this.prefix) .write(this.declarationId) .write(this.isAnonymousDefault) .write(this.constValue); super.serialize(context); } /** * Restores this instance from the provided deserializer context. * @param {ObjectDeserializerContext} context context */ deserialize(context) { this.range = context.read(); const c1 = context.rest; this.rangeStatement = c1.read(); const c2 = c1.rest; this.prefix = c2.read(); const c3 = c2.rest; this.declarationId = c3.read(); const c4 = c3.rest; this.isAnonymousDefault = c4.read(); const c5 = c4.rest; this.constValue = c5.read(); super.deserialize(c5.rest); } } makeSerializable( HarmonyExportExpressionDependency, "webpack/lib/dependencies/HarmonyExportExpressionDependency" ); HarmonyExportExpressionDependency.Template = class HarmonyExportDependencyTemplate extends ( NullDependency.Template ) { /** * Applies the plugin by registering its hooks on the compiler. * @param {Dependency} dependency the dependency for which the template should be applied * @param {ReplaceSource} source the current replace source which can be modified * @param {DependencyTemplateContext} templateContext the context object * @returns {void} */ apply(dependency, source, templateContext) { const { module, moduleGraph, runtimeTemplate, runtimeRequirements, initFragments, runtime } = templateContext; // a wrapped module's default binding is unreachable from the shared scope const concatenationScope = templateContext.concatenationScope && !templateContext.concatenationScope.isWrapped() ? templateContext.concatenationScope : undefined; const dep = /** @type {HarmonyExportExpressionDependency} */ (dependency); const { declarationId } = dep; const exportsName = module.exportsArgument; if (declarationId) { /** @type {string} */ let name; if (typeof declarationId === "string") { name = declarationId; } else { name = ConcatenationScope.DEFAULT_EXPORT; source.replace( declarationId.range[0], declarationId.range[1] - 1, `${declarationId.prefix}${name}${declarationId.suffix}` ); } const used = concatenationScope ? undefined : moduleGraph .getExportsInfo(module) .getUsedName(DEFAULT_EXPORT_NAME, runtime); if (concatenationScope) { concatenationScope.registerExport(DEFAULT_EXPORT_NAME, name); } else if (used) { /** @type {ExportMap} */ const map = new Map(); map.set(used, `/* export default binding */ ${name}`); initFragments.push(new HarmonyExportInitFragment(exportsName, map)); } source.replace( dep.rangeStatement[0], dep.range[0] - 1, `/* harmony default export */ ${dep.prefix}` ); if ( typeof declarationId !== "string" && dep.isAnonymousDefault && (concatenationScope || used) ) { // Fix .name for anonymous default export function declarations // see test/test262-cases/test/language/module-code/instn-named-bndng-dflt-fun-anon.js cspell:disable-line runtimeRequirements.add(RuntimeGlobals.setAnonymousDefaultName); initFragments.push( new InitFragment( `${RuntimeGlobals.setAnonymousDefaultName}(${name});\n`, InitFragment.STAGE_HARMONY_EXPORTS, 2 ) ); } } else { /** @type {string} */ let content; let name = ConcatenationScope.DEFAULT_EXPORT; let defaultIsUsed = Boolean(concatenationScope); if (runtimeTemplate.supportsConst()) { content = `/* harmony default export */ const ${name} = `; if (concatenationScope) { concatenationScope.registerExport(DEFAULT_EXPORT_NAME, name); } else { const used = moduleGraph .getExportsInfo(module) .getUsedName(DEFAULT_EXPORT_NAME, runtime); if (used instanceof InlinedUsedName) { content = `/* inlined harmony default export */ const ${name} = `; } else if (used) { defaultIsUsed = true; runtimeRequirements.add(RuntimeGlobals.exports); // `export default <expr>` binds an immutable const declaration const canUseValueBinding = /** @type {import("../Module").BuildInfo} */ (module.buildInfo) .isCircular === false; if (canUseValueBinding) { initFragments.push( new ExportBindingInitFragment( exportsName, [ { name: used, value: `/* export default binding */ ${name}`, bindingType: "value" } ], true ) ); } else { /** @type {ExportMap} */ const map = new Map(); map.set(used, name); initFragments.push( new HarmonyExportInitFragment(exportsName, map) ); } } else { content = `/* unused harmony default export */ var ${name} = `; } } } else if (concatenationScope) { content = `/* harmony default export */ var ${name} = `; concatenationScope.registerExport(DEFAULT_EXPORT_NAME, name); } else { const used = moduleGraph .getExportsInfo(module) .getUsedName(DEFAULT_EXPORT_NAME, runtime); if (used instanceof InlinedUsedName) { content = `/* inlined harmony default export */ var ${name} = `; } else if (used) { defaultIsUsed = true; runtimeRequirements.add(RuntimeGlobals.exports); // This is a little bit incorrect as TDZ is not correct, but we can't use const. // No local `__WEBPACK_DEFAULT_EXPORT__` binding is created in this path, // so the anonymous-default `.name` fix-up below must reference the actual // assignment target instead. See issue #20793. name = `${exportsName}${propertyAccess( typeof used === "string" ? [used] : used )}`; content = `/* harmony default export */ ${name} = `; } else { content = `/* unused harmony default export */ var ${name} = `; } } if (dep.range) { source.replace( dep.rangeStatement[0], dep.range[0] - 1, `${content}(${dep.prefix}` ); if (dep.isAnonymousDefault && defaultIsUsed) { // Fix .name for anonymous default export expressions // see test/test262-cases/test/language/module-code/eval-export-dflt-cls-anon.js cspell:disable-line runtimeRequirements.add(RuntimeGlobals.setAnonymousDefaultName); source.replace( dep.range[1], dep.rangeStatement[1] - 0.5, `);\n${RuntimeGlobals.setAnonymousDefaultName}(${name});` ); } else { source.replace(dep.range[1], dep.rangeStatement[1] - 0.5, ");"); } return; } source.replace(dep.rangeStatement[0], dep.rangeStatement[1] - 1, content); } } }; module.exports = HarmonyExportExpressionDependency;