Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

feat: experimental module splitting for async-only exports#21462

Open
alexander-akait wants to merge 2 commits into
mainwebpack/webpack:mainfrom
claude/issue-20537-investigation-noT6Jwebpack/webpack:claude/issue-20537-investigation-noT6JCopy head branch name to clipboard
Open

feat: experimental module splitting for async-only exports#21462
alexander-akait wants to merge 2 commits into
mainwebpack/webpack:mainfrom
claude/issue-20537-investigation-noT6Jwebpack/webpack:claude/issue-20537-investigation-noT6JCopy head branch name to clipboard

Conversation

@alexander-akait

Copy link
Copy Markdown
Member

Summary

A module is the atomic unit of chunk placement and export usage is tracked per-runtime, so an export reached only through an async boundary still lands in the initial chunk (Refs #20537). This adds an opt-in experiments.moduleSplitting (off by default) that moves self-contained, side-effect-free exports into synthetic part-modules — and, for namespace consumers (import() / import *), behind a synthetic namespace facade — so those exports follow the async chunk instead of inflating the initial one. It also resolves barrel re-exports to their origin via ExportInfo.getTarget. This covers the tractable subset; the full vue-loader reproduction (an assembled, impure component) still needs general statement-level fragmentation and is intentionally left to a follow-up.

What kind of change does this PR introduce?

feat

Did you add tests for your changes?

Yes — test/configCases/module-splitting/* (12 cases): named/default export split, dynamic-import and import * namespace facades, barrel re-export, the sideEffects/source-analysis gate, the include/exclude + module.rules flag + /* webpackSplit */ controls, and let/live-binding safety.

Does this PR introduce a breaking change?

No. Everything is behind experiments.moduleSplitting (off by default); when disabled the plugin is never registered, so there is no cost or behavior change for existing builds.

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

The new experiments.moduleSplitting option, its Turbopack-style sideEffects gate, and the controls for which modules split ({ include, exclude }, the module.rules moduleSplitting flag, and the /* webpackSplit */ source hint).

Use of AI

Yes. This change was written with Claude (Claude Code); the design, implementation, and tests were AI-assisted and human-directed and reviewed. It reuses existing webpack machinery (JavascriptParser.isPure, ExportInfo.getTarget, ModuleGraph connection APIs) rather than introducing new analysis, and is gated behind an experiment. Per the webpack AI policy, flagging it explicitly.


Generated by Claude Code

Copilot AI review requested due to automatic review settings July 21, 2026 06:34
@changeset-bot

changeset-bot Bot commented Jul 21, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 83d4a9e

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

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

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

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

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

Install it locally:

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

.getExportsInfo(entry.sourceModule)
.getUsedName(entry.sourceName, runtime) || entry.sourceName;
const access = `${varName}[${JSON.stringify(used)}]`;
definitions.push(`${JSON.stringify(entry.exportName)}: () => ${access}`);
@codecov

codecov Bot commented Jul 21, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 99.46333% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 93.50%. Comparing base (d3c2c4e) to head (83d4a9e).

Files with missing lines Patch % Lines
lib/optimize/ModuleSplittingPlugin.js 99.51% 2 Missing ⚠️
lib/optimize/NamespaceFacadeModule.js 98.80% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #21462      +/-   ##
==========================================
+ Coverage   93.45%   93.50%   +0.05%     
==========================================
  Files         614      617       +3     
  Lines       72417    72976     +559     
  Branches    20780    20941     +161     
==========================================
+ Hits        67678    68239     +561     
+ Misses       4739     4737       -2     
Flag Coverage Δ
css-parsing 25.58% <50.00%> (+<0.01%) ⬆️
html5lib 27.18% <50.00%> (+<0.01%) ⬆️
integration 89.64% <93.38%> (+0.03%) ⬆️
test262 43.12% <50.00%> (+0.02%) ⬆️
unit 46.13% <58.50%> (+0.02%) ⬆️

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.

Add experiments.moduleSplitting (off by default): move self-contained,
side-effect-free exports that are only reached through an async boundary out of
the initial chunk into synthetic part-modules, so they follow the async chunk
instead of inflating the initial one.

- Named and `export default` exports; exports consumed via dynamic import() /
  import * behind a synthetic namespace facade; barrel re-exports resolved to
  their origin via ExportInfo.getTarget.
- Turbopack-style gate: only split modules proven side-effect-free (by the
  `sideEffects` declaration or source analysis). Override which modules split via
  experiments.moduleSplitting { include, exclude }, a `module.rules`
  `moduleSplitting` flag, or a `/* webpackSplit */` source hint.
- Reuses JavascriptParser.isPure (no re-parse); the plugin is registered only
  when the experiment is enabled, so non-users pay nothing.

Covered by test/configCases/module-splitting/* (12 cases).
@alexander-akait
alexander-akait force-pushed the claude/issue-20537-investigation-noT6J branch from 4aa7165 to d250030 Compare July 21, 2026 06:43
Copilot AI review requested due to automatic review settings July 21, 2026 06:43

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@codspeed-hq

codspeed-hq Bot commented Jul 21, 2026

Copy link
Copy Markdown

Merging this PR will regress 1 benchmark

⚡ 2 improved benchmarks
❌ 1 regressed benchmark
✅ 213 untouched benchmarks

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Mode Benchmark BASE HEAD Efficiency
Memory benchmark "devtool-eval", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' 129 KB 330.4 KB -60.95%
Memory benchmark "concatenate-modules", scenario '{"name":"mode-development","mode":"development"}' 4.9 MB 1.6 MB ×3
Memory benchmark "many-modules-esm", scenario '{"name":"mode-production","mode":"production"}' 10 MB 7.6 MB +30.96%

Tip

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


Comparing claude/issue-20537-investigation-noT6J (83d4a9e) with main (d3c2c4e)

Open in CodSpeed

Copilot AI review requested due to automatic review settings July 21, 2026 07:11

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Fix a Node 10 incompatibility in the sideeffect-source fixture (globalThis
is undefined there) by using a plain statement-level side effect, and add
coverage for the split analyzer's AST-shape branches (ast-shapes fixture)
and the synthetic part/facade modules' serialize and needBuild paths
(ModuleSplitting.unittest.js).
@alexander-akait
alexander-akait force-pushed the claude/issue-20537-investigation-noT6J branch from 541b54e to 83d4a9e Compare July 21, 2026 07:17
Copilot AI review requested due to automatic review settings July 21, 2026 07:17

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions

Copy link
Copy Markdown
Contributor

Types Coverage

Coverage after merging claude/issue-20537-investigation-noT6J into main will be
99.34%
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.js100%100%100%100%
examples/markdown
   webpack.config.mjs100%100%100%100%
examples/module-federation
   test.filter.js100%100%100%100%
examples/reexport-components
   test.filter.js100%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-emscripten
   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%
   CircularModulesPlugin.js98.81%100%100%98.81%136
   CleanPlugin.js99.12%100%100%99.12%207, 227
   CodeGenerationResults.js100%100%100%100%
   CompatibilityPlugin.js100%100%100%100%
   Compilation.js98.42%100%100%98.42%1639, 1958, 1965, 1973, 1995, 1998, 2937, 3416–3417, 3449, 4149, 4179, 4232–4233, 4237, 4242, 4258–4259, 4273–4274, 4279–4280, 4757, 4783, 527, 532, 5591, 5623, 5640, 5656, 5672, 5687, 5712–5713, 5715, 6045, 6050, 6056, 6059, 6066, 6078, 6080, 6084, 6100, 6115, 6147, 6201, 6225, 6340, 778–779
   Compiler.js99.56%100%100%99.56%1147–1148, 1156
   ConcatenationScope.js98.65%100%100%98.65%195
   ConditionalInitFragment.js100%100%100%100%
   ConstPlugin.js100%100%100%100%
   ContextExclusionPlugin.js100%100%100%100%
   ContextModule.js99.88%100%100%99.88%1461
   ContextModuleFactory.js97.20%100%100%97.20%266, 435, 456, 461, 501, 512, 514, 518, 527–528
   ContextReplacementPlugin.js100%100%100%100%
   DefinePlugin.js99.07%100%100%99.07%1048, 176–177, 193, 212, 286
   DependenciesBlock.js100%100%100%100%
   Dependency.js98.51%100%100%98.51%479, 525
   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.65%100%100%98.65%1196, 1199, 514–518, 520, 666
   ExternalModuleFactoryPlugin.js100%100%100%100%
   ExternalsPlugin.js100%100%100%100%
   FileSystemInfo.js99.52%100%100%99.52%182, 2402–2403, 2406, 2417, 2428, 2439, 280, 3876, 3891, 3915
   FlagAllModulesAsUsedPlugin.js100%100%100%100%
   FlagDependencyExportsPlugin.js98.21%100%100%98.21%448, 457, 460, 464, 476
   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%
   LazyBarrel.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%1289, 1294, 1354, 1368, 1430, 1439
   ModuleFactory.js100%100%100%100%
   ModuleFilenameHelpers.js98.85%100%100%98.85%106, 108
   ModuleGraph.js99.73%100%100%99.73%1005
   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%661
   MultiStats.js100%100%100%100%
   MultiWatching.js100%100%100%100%
   NoEmitOnErrorsPlugin.js100%100%100%100%
   NodeStuffPlugin.js100%100%100%100%
   NormalModule.js97.97%100%100%97.97%1008, 1025, 1273, 1307, 1323, 1770, 2067, 2072–2082, 34, 988, 991
   NormalModuleFactory.js98.73%100%100%98.73%1118, 1386, 1397, 1407, 1458–1460, 1467, 521, 533
   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.js99.80%100%100%99.80%688
   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, 889
   Stats.js100%100%100%100%
   Template.js100%100%100%100%
   TemplatedPathPlugin.js99.43%100%100%99.43%308–309
   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%371
   cli.js98.63%100%100%98.63%10, 119, 549, 581, 631, 905
   index.js99.72%100%100%99.72%184
   validateSchema.js94.67%100%100%94.67%100, 87, 89, 98
   webpack.js97.10%100%100%97.10%10, 263, 285, 287
lib/asset
   AssetBytesGenerator.js100%100%100%100%
   AssetBytesParser.js100%100%100%100%
   AssetGenerator.js100%100%100%100%
   AssetModule.js100%100%100%100%
   AssetModulesPlugin.js97.94%100%100%97.94%294, 318, 321, 42, 451, 47
   AssetParser.js100%100%100%100%
   AssetSourceGenerator.js100%100%100%100%
   AssetSourceParser.js100%100%100%100%
   RawDataUrlModule.js100%100%100%100%
   WebManifestGenerator.js100%100%100%100%
   WebManifestParser.js100%100%100%100%
lib/async-modules
   AsyncModuleHelpers.js100%100%100%100%
   AwaitDependenciesInitFragment.js100%100%100%100%
   InferAsyncModulesPlugin.js100%100%100%100%
   isGeneratorLowered.js100%100%100%100%
lib/bun
   BunTargetPlugin.js100%100%100%100%
lib/cache
   AddBuildDependenciesPlugin.js100%100%100%100%
   AddManagedPathsPlugin.js100%100%100%100%
   IdleFileCachePlugin.js97.92%100%100%97.92%75, 87, 95
   MemoryCachePlugin.js95.83%100%100%95.83%33
   MemoryWithGcCachePlugin.js93.15%100%100%93.15%107, 114–115, 123, 90
   PackFileCacheStrategy.js96.40%100%100%96.40%1251, 1351, 1355, 1417, 628, 647, 657–659, 661, 677–678, 683, 686, 688, 693, 698, 723, 729, 763, 769, 775, 780, 791, 800, 805–806, 808, 825, 831–832, 834
   ResolverCachePlugin.js100%100%100%100%
   getLazyHashedEtag.js100%100%100%100%
   mergeEtags.js100%100%100%100%
lib/config
   browserslistTargetHandler.js100%100%100%100%
   defaults.js99.36%100%100%99.36%1584–1586, 1594,

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.

3 participants

Morty Proxy This is a proxified and sanitized view of the page, visit original site.