Skip to content

fix: remove unnecessary __webpack_require__ in ESM library output#21032

Merged
alexander-akait merged 2 commits into
mainfrom
fix/unnecessary-webpack-require-esm-library
May 25, 2026
Merged

fix: remove unnecessary __webpack_require__ in ESM library output#21032
alexander-akait merged 2 commits into
mainfrom
fix/unnecessary-webpack-require-esm-library

Conversation

@xiaoxiaojx

Copy link
Copy Markdown
Member

Summary

When an ESM library chunk (output.library.type = "module") contains non-JS modules (CSS via experiments.css, MiniCssExtractPlugin, asset modules, etc.) alongside a concatenated entry, __webpack_require__ runtime helpers (var __webpack_require__ = {}, __webpack_require__.d, __webpack_require__.o) are emitted even though they are never referenced in the output.

Root cause: ModuleLibraryPlugin's cleanup hook bailed out when getNumberOfChunkModules(chunk) !== 1, but CSS/asset modules increase the count without actually needing those JS helpers.

Two fixes:

  1. HarmonyExportInitFragment.getContent() — only add definePropertyGetters and exports runtime requirements when exportMap is non-empty. Previously these were added unconditionally even when the fragment produced no output.

  2. ModuleLibraryPlugin cleanup hook — replace the coarse getNumberOfChunkModules !== 1 guard with a per-module check: iterate chunk modules and only retain helpers that another module actually requires.

Fixes #21029

What kind of change does this PR introduce?

fix

Did you add tests for your changes?

Yes. Added entry4.js + style.css to the existing test/configCases/library/module-useless-export-requirement/ test case, covering the ESM library + CSS module combination. Updated 4 HTML snapshot files that previously contained the unnecessary helpers.

Does this PR introduce a breaking change?

No. This only removes dead runtime code from the output.

If relevant, what needs to be documented once your changes are merged or what have you already documented?

n/a

Use of AI

Claude Code was used to investigate the root cause, draft the implementation, and write the test case, under human review.

@changeset-bot

changeset-bot Bot commented May 24, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: bd64378

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
webpack Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@linux-foundation-easycla

linux-foundation-easycla Bot commented May 24, 2026

Copy link
Copy Markdown

CLA Signed
The committers listed above are authorized under a signed CLA.

  • ✅ login: xiaoxiaojx / name: xiaoxiaojx (3bc5646)

@github-actions

github-actions Bot commented May 24, 2026

Copy link
Copy Markdown
Contributor

This PR is packaged and the instant preview is available (cc4035b).

Install it locally:

  • npm
npm i -D webpack@https://pkg.pr.new/webpack@cc4035b
  • yarn
yarn add -D webpack@https://pkg.pr.new/webpack@cc4035b
  • pnpm
pnpm add -D webpack@https://pkg.pr.new/webpack@cc4035b

@codecov

codecov Bot commented May 24, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 86.36364% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 91.57%. Comparing base (12cb825) to head (bd64378).

Files with missing lines Patch % Lines
lib/library/ModuleLibraryPlugin.js 82.35% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #21032      +/-   ##
==========================================
- Coverage   91.58%   91.57%   -0.02%     
==========================================
  Files         573      573              
  Lines       59541    59555      +14     
  Branches    16077    16084       +7     
==========================================
+ Hits        54532    54536       +4     
- Misses       5009     5019      +10     
Flag Coverage Δ
integration 89.49% <86.36%> (-0.02%) ⬇️
test262 45.31% <100.00%> (-0.01%) ⬇️
unit 37.95% <0.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

When a chunk contains non-JS modules (CSS, assets) alongside a
concatenated ESM entry, the runtime cleanup hook in ModuleLibraryPlugin
bailed out because getNumberOfChunkModules > 1, leaving unused
__webpack_require__.d and __webpack_require__.o helpers in the output.

- HarmonyExportInitFragment: only add definePropertyGetters requirement
  when exportMap is non-empty
- ModuleLibraryPlugin: replace coarse getNumberOfChunkModules check with
  per-module requirement inspection

Closes #21029
@xiaoxiaojx xiaoxiaojx force-pushed the fix/unnecessary-webpack-require-esm-library branch from 5dd30f5 to 3bc5646 Compare May 24, 2026 09:22
@codspeed-hq

codspeed-hq Bot commented May 24, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

⚡ 2 improved benchmarks
❌ 4 regressed benchmarks
✅ 138 untouched benchmarks
⏩ 72 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Mode Benchmark BASE HEAD Efficiency
Memory benchmark "many-chunks-commonjs", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' 446.6 KB 253.8 KB +76.01%
Memory benchmark "side-effects-reexport", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' 1,021.2 KB 1,364.1 KB -25.14%
Memory benchmark "json-modules", scenario '{"name":"mode-development","mode":"development"}' 518 KB 845.9 KB -38.77%
Memory benchmark "many-chunks-esm", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' 167.1 KB 248.3 KB -32.7%
Memory benchmark "future-defaults", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' 144.2 KB 241.4 KB -40.25%
Memory benchmark "concatenate-modules", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' 159.6 KB 131 KB +21.78%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing fix/unnecessary-webpack-require-esm-library (bd64378) with main (12cb825)

Open in CodSpeed

Footnotes

  1. 72 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@github-actions

Copy link
Copy Markdown
Contributor

Types Coverage

Coverage after merging fix/unnecessary-webpack-require-esm-library into main will be
98.96%
Coverage Report
FileStmtsBranchesFuncsLinesUncovered Lines
bin
   webpack.js98.77%100%100%98.77%91
examples
   build-common.js100%100%100%100%
   buildAll.js100%100%100%100%
   examples.js100%100%100%100%
   template-common.js98.21%100%100%98.21%72
examples/custom-javascript-parser
   test.filter.js100%100%100%100%
examples/custom-javascript-parser/internals
   acorn-parse.js100%100%100%100%
   meriyah-parse.js100%100%100%100%
   oxc-parse.js91.30%100%100%91.30%140, 142–143, 145, 147, 153–154, 161, 168, 90
examples/markdown
   webpack.config.mjs100%100%100%100%
examples/typescript
   test.filter.js100%100%100%100%
examples/typescript-non-erasable
   test.filter.js50%100%100%50%5
examples/virtual-modules
   test.filter.js100%100%100%100%
examples/wasm-bindgen-esm
   test.filter.js100%100%100%100%
examples/wasm-complex
   test.filter.js100%100%100%100%
examples/wasm-simple
   test.filter.js100%100%100%100%
examples/wasm-simple-source-phase
   test.filter.js100%100%100%100%
lib
   APIPlugin.js100%100%100%100%
   AsyncDependenciesBlock.js100%100%100%100%
   AutomaticPrefetchPlugin.js100%100%100%100%
   BannerPlugin.js100%100%100%100%
   Cache.js98.21%100%100%98.21%101
   CacheFacade.js100%100%100%100%
   Chunk.js99.72%100%100%99.72%39
   ChunkGraph.js100%100%100%100%
   ChunkGroup.js100%100%100%100%
   ChunkTemplate.js100%100%100%100%
   CleanPlugin.js99.15%100%100%99.15%206, 226
   CodeGenerationResults.js100%100%100%100%
   CompatibilityPlugin.js100%100%100%100%
   Compilation.js98.45%100%100%98.45%1572, 1868, 1875, 1883, 1905, 2801, 3226, 3888, 3917, 3970–3971, 3975, 3980, 3996–3997, 4011–4012, 4017–4018, 4495, 4521, 511, 516, 5229, 5261, 5278, 5294, 5310, 5325, 5350–5351, 5353, 5681, 5686, 5692, 5695, 5707, 5709, 5713, 5729, 5744, 5776, 5830, 5854, 5968, 730–731
   Compiler.js99.55%100%100%99.55%1116–1117, 1125
   ConcatenationScope.js98.59%100%100%98.59%189
   ConditionalInitFragment.js100%100%100%100%
   ConstPlugin.js100%100%100%100%
   ContextExclusionPlugin.js100%100%100%100%
   ContextModule.js100%100%100%100%
   ContextModuleFactory.js97.40%100%100%97.40%258, 395, 418, 420, 424, 433–434
   ContextReplacementPlugin.js100%100%100%100%
   DefinePlugin.js98.92%100%100%98.92%158–159, 175, 194, 268
   DependenciesBlock.js100%100%100%100%
   Dependency.js98.20%100%100%98.20%379, 425
   DependencyTemplate.js100%100%100%100%
   DependencyTemplates.js100%100%100%100%
   DotenvPlugin.js98.41%100%100%98.41%378, 391–392
   DynamicEntryPlugin.js100%100%100%100%
   EntryOptionPlugin.js100%100%100%100%
   EntryPlugin.js100%100%100%100%
   Entrypoint.js100%100%100%100%
   EnvironmentPlugin.js97.14%100%100%97.14%49
   ErrorHelpers.js100%100%100%100%
   EvalDevToolModulePlugin.js100%100%100%100%
   EvalSourceMapDevToolPlugin.js100%100%100%100%
   ExportsInfo.js100%100%100%100%
   ExportsInfoApiPlugin.js100%100%100%100%
   ExternalModule.js98.97%100%100%98.97%425–429, 577
   ExternalModuleFactoryPlugin.js100%100%100%100%
   ExternalsPlugin.js100%100%100%100%
   FileSystemInfo.js99.50%100%100%99.50%182, 2252–2253, 2256, 2267, 2278, 2289, 278, 3694, 3709, 3733
   FlagAllModulesAsUsedPlugin.js100%100%100%100%
   FlagDependencyExportsPlugin.js98.74%100%100%98.74%399, 401, 405
   FlagDependencyUsagePlugin.js100%100%100%100%
   FlagEntryExportAsUsedPlugin.js100%100%100%100%
   Generator.js100%100%100%100%
   HotModuleReplacementPlugin.js100%100%100%100%
   HotUpdateChunk.js100%100%100%100%
   IgnorePlugin.js100%100%100%100%
   IgnoreWarningsPlugin.js100%100%100%100%
   InitFragment.js100%100%100%100%
   JavascriptMetaInfoPlugin.js100%100%100%100%
   LibraryTemplatePlugin.js100%100%100%100%
   LoaderOptionsPlugin.js100%100%100%100%
   LoaderTargetPlugin.js100%100%100%100%
   MainTemplate.js100%100%100%100%
   ManifestPlugin.js100%100%100%100%
   Module.js98.50%100%100%98.50%1305, 1310, 1371, 1385, 1447, 1456
   ModuleFactory.js100%100%100%100%
   ModuleFilenameHelpers.js98.85%100%100%98.85%106, 108
   ModuleGraph.js99.73%100%100%99.73%1004
   ModuleGraphConnection.js100%100%100%100%
   ModuleInfoHeaderPlugin.js100%100%100%100%
   ModuleNotFoundError.js100%100%100%100%
   ModuleProfile.js100%100%100%100%
   ModuleSourceTypeConstants.js100%100%100%100%
   ModuleTemplate.js100%100%100%100%
   ModuleTypeConstants.js100%100%100%100%
   MultiCompiler.js99.69%100%100%99.69%645
   MultiStats.js100%100%100%100%
   MultiWatching.js100%100%100%100%
   NoEmitOnErrorsPlugin.js100%100%100%100%
   NodeStuffPlugin.js100%100%100%100%
   NormalModule.js98.11%100%100%98.11%1208, 1211, 1228, 1245, 1492, 1526, 1542, 1629, 2252, 2257–2267, 569
   NormalModuleFactory.js99.47%100%100%99.47%1083, 1392, 486, 498
   NormalModuleReplacementPlugin.js100%100%100%100%
   NullFactory.js100%100%100%100%
   OptimizationStages.js100%100%100%100%
   OptionsApply.js100%100%100%100%
   Parser.js100%100%100%100%
   PlatformPlugin.js100%100%100%100%
   PrefetchPlugin.js100%100%100%100%
   ProgressPlugin.js98.85%100%100%98.85%519–520, 525, 527, 591
   ProvidePlugin.js100%100%100%100%
   RawModule.js100%100%100%100%
   RecordIdsPlugin.js100%100%100%100%
   RequestShortener.js100%100%100%100%
   ResolverFactory.js100%100%100%100%
   RuntimeGlobals.js100%100%100%100%
   RuntimeModule.js100%100%100%100%
   RuntimePlugin.js100%100%100%100%
   RuntimeTemplate.js100%100%100%100%
   SelfModuleFactory.js100%100%100%100%
   SingleEntryPlugin.js100%100%100%100%
   SourceMapDevToolModuleOptionsPlugin.js100%100%100%100%
   SourceMapDevToolPlugin.js98.62%100%100%98.62%220, 224, 226, 419, 430, 891
   Stats.js100%100%100%100%
   Template.js100%100%100%100%
   TemplatedPathPlugin.js98.86%100%100%98.86%136–137
   UseStrictPlugin.js100%100%100%100%
   WarnCaseSensitiveModulesPlugin.js100%100%100%100%
   WarnDeprecatedOptionPlugin.js100%100%100%100%
   WarnNoModeSetPlugin.js100%100%100%100%
   WatchIgnorePlugin.js100%100%100%100%
   Watching.js100%100%100%100%
   WebpackError.js100%100%100%100%
   WebpackIsIncludedPlugin.js100%100%100%100%
   WebpackOptionsApply.js100%100%100%100%
   WebpackOptionsDefaulter.js100%100%100%100%
   buildChunkGraph.js99.87%100%100%99.87%325
   cli.js98.46%100%100%98.46%10, 119, 471, 503, 545, 815
   index.js99.72%100%100%99.72%165
   validateSchema.js94.67%100%100%94.67%100, 87, 89, 98
   webpack.js96.33%100%100%96.33%10, 198, 220, 222
lib/asset
   AssetBytesGenerator.js100%100%100%100%
   AssetBytesParser.js100%100%100%100%
   AssetGenerator.js100%100%100%100%
   AssetModulesPlugin.js97.32%100%100%97.32%283, 307, 310, 36, 362, 41
   AssetParser.js100%100%100%100%
   AssetSourceGenerator.js100%100%100%100%
   AssetSourceParser.js100%100%100%100%
   RawDataUrlModule.js100%100%100%100%
lib/async-modules
   AsyncModuleHelpers.js100%100%100%100%
   AwaitDependenciesInitFragment.js100%100%100%100%
   InferAsyncModulesPlugin.js100%100%100%100%
lib/cache
   AddBuildDependenciesPlugin.js100%100%100%100%
   AddManagedPathsPlugin.js100%100%100%100%
   IdleFileCachePlugin.js97.92%100%100%97.92%71, 83, 91
   MemoryCachePlugin.js95.83%100%100%95.83%33
   MemoryWithGcCachePlugin.js93.15%100%100%93.15%106, 113–114, 122, 89
   PackFileCacheStrategy.js96.40%100%100%96.40%1250, 1350, 1354, 1416, 628, 647, 657–659, 661, 677–678, 683, 686, 688, 693, 698, 722, 728, 762, 768, 774, 779, 790, 799, 804–805, 807, 824, 830–831, 833
   ResolverCachePlugin.js100%100%100%100%
   getLazyHashedEtag.js100%100%100%100%
   mergeEtags.js100%100%100%100%
lib/config
   browserslistTargetHandler.js100%100%100%100%
   defaults.js99.29%100%100%99.29%1411–1413, 1421, 271, 274, 279, 283
   normalization.js99%100%100%99%191–192, 258, 273
   target.js100%100%100%100%
lib/container
   ContainerEntryDependency.js100%100%100%100%
   ContainerEntryModule.js100%100%100%100%
   ContainerEntryModuleFactory.js100%100%100%100%
   ContainerExposedDependency.js100%100%100%100%
   ContainerPlugin.js100%100%100%100%
   ContainerReferencePlugin.js100%100%100%100%
   FallbackDependency.js100%100%100%100%
   

@alexander-akait alexander-akait merged commit cc4035b into main May 25, 2026
58 of 61 checks passed
@alexander-akait alexander-akait deleted the fix/unnecessary-webpack-require-esm-library branch May 25, 2026 12:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

var __webpack_require__ = {}; unnecessary emitted

2 participants