fix(library): aggregate exports from all entry modules (fixes #15936)#20447
fix(library): aggregate exports from all entry modules (fixes #15936)#20447vvinit594 wants to merge 1 commit intowebpack:mainfrom
Conversation
…#15936) When entry is an array (e.g. entry: ['a', 'b', 'c']) and output.library is set, only the last entry module's exports were used. Now all entry modules' exports are merged into the library output. - AbstractLibraryPlugin: call finishEntryModule for every entry dependency, not only deps[deps.length - 1], so all entry modules are marked as used for library export. - JavascriptModulesPlugin (bootstrap): when library is set and there are multiple entry modules, pass the same exports object to every entry module so they merge (non-inlined path). - JavascriptModulesPlugin (inlined): when library is set and multiple entry modules are inlined, use the shared __webpack_exports__ for all so they merge into one object. Added test configCases/library/multiple-entry-exports-15936. Co-authored-by: Cursor <cursoragent@cursor.com>
|
|
|
There was a problem hiding this comment.
Pull request overview
This PR fixes a bug where only the last entry module's exports were exposed when using output.library with multiple entry modules (e.g., entry: ["a", "b", "c"]). The fix ensures that all entry modules' exports are aggregated into the library object by: (1) calling finishEntryModule for all entry dependencies in AbstractLibraryPlugin, not just the last one, and (2) passing the same shared exports object to all entry modules in JavascriptModulesPlugin's startup code generation.
Changes:
- AbstractLibraryPlugin now processes all entry dependencies for library export marking
- JavascriptModulesPlugin's bootstrap path shares exports object across all entry modules when library output is configured
- JavascriptModulesPlugin's inlined path adds logic to merge exports from multiple entry modules
- Test case added to verify the fix with three entry modules exporting different constants
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| lib/library/AbstractLibraryPlugin.js | Changed to iterate through all entry dependencies and call finishEntryModule for each, instead of only processing the last dependency |
| lib/javascript/JavascriptModulesPlugin.js | Added mergeLibraryEntryExports flag and logic to share RuntimeGlobals.exports across all entry modules in both inlined and bootstrap code paths |
| test/configCases/library/multiple-entry-exports-15936/webpack.config.js | Test configuration with three entry modules and assign library type |
| test/configCases/library/multiple-entry-exports-15936/test.config.js | Test cleanup to remove global library object |
| test/configCases/library/multiple-entry-exports-15936/a.js | First entry module exporting constant a |
| test/configCases/library/multiple-entry-exports-15936/b.js | Second entry module exporting constant b |
| test/configCases/library/multiple-entry-exports-15936/c.js | Third entry module exporting constant c and containing test assertions |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (mergeLibraryEntryExports) { | ||
| if (m.exportsArgument !== RuntimeGlobals.exports) { | ||
| startupSource.add( | ||
| `var ${m.exportsArgument} = ${RuntimeGlobals.exports};\n` | ||
| ); | ||
| } | ||
| // else: module uses __webpack_exports__ from outer scope |
There was a problem hiding this comment.
When multiple entry modules are inlined with mergeLibraryEntryExports, the existing logic at lines 1020-1025 (not shown in diff) wraps each entry module in an IIFE for isolation. The export variable declarations at lines 1070-1073 are then placed inside these IIFE scopes.
For ES modules that use property assignments (e.g., __webpack_exports__.a = 1), this works because the outer __webpack_exports__ is accessed through scope chain lookup. However, if a module reassigns its exports variable (e.g., exports = {...}), the connection to the shared __webpack_exports__ would be broken, preventing export aggregation.
While ES modules typically only do property assignments, this creates a fragile dependency on module code behavior. Consider explicitly preventing IIFE wrapping when mergeLibraryEntryExports is true, or documenting this limitation.
Pull request body for #15936 (copy into GitHub when opening the PR)
What does this PR do?
Fixes #15936
Problem: With
entry: ["a", "b", "c"]andoutput.library: { type: "global" }(orassign), only exports from the last entry module (c) were exposed on the library. Exports fromaandbwere lost.Cause:
finishEntryModulefordeps[deps.length - 1], so only the last entry module was marked as "used as library export"; the others could be tree-shaken or not wired for export.{}) except the last, so only the last module’s exports were assigned to the library target.Fix:
finishEntryModulefor every entry dependency indeps, not only the last one, so all entry modules are marked as used for library export.output.libraryis set and there are multiple entry modules, pass the same exports object (RuntimeGlobals.exports) to every entry module in the non-inlined startup path so they all merge into one object.output.libraryis set and multiple entry modules are inlined, use the shared__webpack_exports__for all of them (no per-module{}) so they merge; avoid adding a self-referentialvar __webpack_exports__ = __webpack_exports__when the module already usesRuntimeGlobals.exports.Test: Added
test/configCases/library/multiple-entry-exports-15936/withentry: ["./a.js", "./b.js", "./c.js"]andoutput.library: { name: "MyLib", type: "assign" }. The test asserts thatMyLib.a,MyLib.b, andMyLib.care all defined correctly.Checklist