Skip to content

perf: skip side-effect-free modules for nested namespace re-exports#21085

Merged
alexander-akait merged 4 commits into
mainfrom
claude/todo-chunk-graph-GO1OL
Jun 4, 2026
Merged

perf: skip side-effect-free modules for nested namespace re-exports#21085
alexander-akait merged 4 commits into
mainfrom
claude/todo-chunk-graph-GO1OL

Conversation

@alexander-akait

Copy link
Copy Markdown
Member

Resolve the "improve for nested imports" TODO in SideEffectsFlagPlugin:
getTarget stops at a namespace boundary (export * as ns), so a nested id
like ns.x previously left the dependency pointing at an intermediate
side-effect-free module unless the module-processing order happened to
revisit it. Now the chain is resolved fully in a single pass.

The common single-id path is unchanged (no extra allocations or
side-effects lookups); the loop only continues across a namespace
boundary when nested ids remain.

Resolve the "improve for nested imports" TODO in SideEffectsFlagPlugin:
getTarget stops at a namespace boundary (export * as ns), so a nested id
like ns.x previously left the dependency pointing at an intermediate
side-effect-free module unless the module-processing order happened to
revisit it. Now the chain is resolved fully in a single pass.

The common single-id path is unchanged (no extra allocations or
side-effects lookups); the loop only continues across a namespace
boundary when nested ids remain.
Resolve the "improve for export *" TODO in SideEffectsFlagPlugin. A plain
`export *` into a side-effect-free module that is a pure single-star
passthrough (no own exports, exactly one `export *`) is equivalent to a
star into that module's source, so the passthrough can be skipped: move
the re-exported names' targets and repoint the star past the chain.

This lets namespace imports through such chains drop the intermediate
module (e.g. a barrel that only `export *`s another barrel). Named imports
were already resolved directly via per-name target moving; this extends
the same skipping to the star itself and to namespace consumers.

The detection short-circuits cheaply (leaf modules aren't passthroughs),
so the optimize-dependencies phase stays flat on star-heavy graphs.
When many modules import the same names from a shared barrel, the nested
import-specifier branch re-ran exportInfo.getTarget for each consumer even
though the side-effect-free resolution is idempotent within a pass. Cache
it per export info so each name resolves once instead of once per consumer.

Turns the getTarget portion from O(imports) into O(unique names): ~7-12%
off the plugin's own time on barrel-heavy graphs, neutral on graphs with
no repeated imports (the map lookup replaces an equivalent walk). The
cache is pass-scoped and released after optimizeDependencies.
@changeset-bot

changeset-bot Bot commented Jun 3, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: c2024b3

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

@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

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

Install it locally:

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

@codecov

codecov Bot commented Jun 3, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.68%. Comparing base (16b9738) to head (c2024b3).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #21085      +/-   ##
==========================================
+ Coverage   91.66%   91.68%   +0.02%     
==========================================
  Files         581      581              
  Lines       61089    61137      +48     
  Branches    16659    16679      +20     
==========================================
+ Hits        55995    56053      +58     
+ Misses       5094     5084      -10     
Flag Coverage Δ
integration 89.59% <100.00%> (+0.02%) ⬆️
test262 45.30% <41.66%> (-0.04%) ⬇️
unit 37.70% <6.25%> (+0.05%) ⬆️

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

☔ View full report in Codecov by Harness.
📢 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.

@codspeed-hq

codspeed-hq Bot commented Jun 3, 2026

Copy link
Copy Markdown

Merging this PR will improve performance by 23.78%

⚡ 3 improved benchmarks
❌ 1 regressed benchmark
✅ 140 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 "asset-modules-bytes", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' 245.1 KB 858.7 KB -71.46%
Memory benchmark "asset-modules-inline", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' 1,231.5 KB 221.9 KB ×5.6
Memory benchmark "devtool-eval", scenario '{"name":"mode-production","mode":"production"}' 7.4 MB 6.1 MB +21.77%
Memory benchmark "css-modules", scenario '{"name":"mode-production","mode":"production"}' 8.9 MB 7.3 MB +21.67%

Tip

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


Comparing claude/todo-chunk-graph-GO1OL (c2024b3) with main (0711964)

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.

The nested-imports loop added earlier produced no observable improvement:
optimizeIncomingConnections already recurses into a connection's origin, so
the original single-pass branch resolves nested namespace re-exports (ns.x,
ns.deep.y, export * as ns) to the same target and same module set in every
case tested. Restore the original branch and keep only the pass-scoped
re-export resolution cache on top of it.

export * passthrough elimination and the cache are unaffected.
@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Types Coverage

Coverage after merging claude/todo-chunk-graph-GO1OL into main will be
98.98%
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.48%100%100%98.48%1576, 1872, 1879, 1887, 1909, 2805, 3230, 3894, 3924, 3977–3978, 3982, 3987, 4003–4004, 4018–4019, 4024–4025, 4502, 4528, 511, 516, 5336, 5368, 5385, 5401, 5417, 5432, 5457–5458, 5460, 5788, 5793, 5799, 5802, 5814, 5816, 5820, 5836, 5851, 5883, 5937, 5961, 6075, 730–731
   Compiler.js99.56%100%100%99.56%1135–1136, 1144
   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.js99%100%100%99%170–171, 187, 206, 280
   DependenciesBlock.js100%100%100%100%
   Dependency.js98.20%100%100%98.20%384, 430
   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.85%100%100%98.85%434, 436, 440
   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.49%100%100%98.49%1304, 1309, 1369, 1383, 1445, 1454
   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%659
   MultiStats.js100%100%100%100%
   MultiWatching.js100%100%100%100%
   NoEmitOnErrorsPlugin.js100%100%100%100%
   NodeStuffPlugin.js100%100%100%100%
   NormalModule.js98.15%100%100%98.15%1212, 1215, 1232, 1249, 1496, 1530, 1546, 1633, 2276, 2281–2291, 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%326
   cli.js98.50%100%100%98.50%10, 119, 487, 519, 569, 839
   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.30%100%100%99.30%1427–1429, 1437, 272, 275, 280, 284
   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 2a7c5ca into main Jun 4, 2026
63 of 64 checks passed
@alexander-akait alexander-akait deleted the claude/todo-chunk-graph-GO1OL branch June 4, 2026 08:20
hai-x pushed a commit that referenced this pull request Jun 4, 2026
hai-x pushed a commit that referenced this pull request Jun 4, 2026
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.

1 participant