Repository navigation
Description
Have you used AI?
Yes. AI assistance was used to reduce the reproduction, inspect the emitted code, and draft this report. The reproduction and configuration comparisons were executed in real builds.
Which part of webpack is affected?
optimization (tree-shaking, splitChunks, concatenation)
Bug Description
With optimization.concatenateModules: true, a dependency of a lazily required feature is evaluated before the application initializes its runtime configuration. The dependency is a separate concatenated module (settings.js + runtime-config.js). Disabling concatenation, or disabling only CommonJS concatenation, preserves the expected execution order.
This is a standalone reproduction using ordinary ESM and CommonJS files. It has no framework, loaders, plugins, Babel, eval, or sideEffects metadata. Minification is disabled in all cases.
Link to Minimal Reproduction and steps to reproduce
Reproduction commit: https://github.com/mackwang112/webpack-demo2/tree/e66f37921f3ab20ef802901cdaf6e8269a60d5ff
git clone https://github.com/mackwang112/webpack-demo2.git
cd webpack-demo2
git checkout e66f37921f3ab20ef802901cdaf6e8269a60d5ff
npm ci
npm run build
npm startCompilation succeeds; running the bundle fails before the first initialization log.
To compare, change concatenateModules: true in webpack.config.cjs to false, then run npm run build and npm start again. Repeat with { commonjs: false }.
| concatenateModules | Runtime result |
|---|---|
true |
Throws before configuration initialization |
false |
Runs successfully in the expected order |
{ commonjs: false } |
Runs successfully in the expected order |
Expected Behavior
The entry requires app.js, which initializes globalThis.APP_CONFIG and exports an object containing start(). Only after that require returns does the entry call app.start(). The require('./reports') inside start() should then evaluate the feature and its dependencies.
Expected output:
1. Configuration initialized
2. Loading the reports feature
3. Reports ready: https://api.example.test (API: https://api.example.test)
Actual Behavior
The bundle throws before any of those logs:
Error: Feature configuration evaluated before app initialization
In dist/bundle.cjs, the settings concatenation is required before the body of app.js initializes the configuration:
// EXTERNAL MODULE: ./src/settings.js + 1 modules
var settings = __webpack_require__(260);
// ./src/app.js
globalThis.APP_CONFIG = { apiBase: 'https://api.example.test' }
console.log('1. Configuration initialized')The reports module itself remains behind a lazy __webpack_require__.cw accessor, but its settings dependency does not.
Environment
From webpack info (local executable paths omitted):
OS: macOS 26.6.2
CPU: arm64 Apple M3 Pro
Node: 26.7.0
npm: 11.19.0
webpack: 5.111.1
webpack-cli: 7.2.3
The same scenario also reproduced with Webpack 5.110.3 on Node 24.11.1. The repository pins Webpack 5.111.1.
Is this a regression?
The last working release has not been established. The observed difference is between the concatenation settings above.
Additional Context
The dependency structure is:
index.js -> require app.js -> call app.start()
app.start() -> require reports.js
reports.js -> import settings.js -> import runtime-config.js
reports.js -> import legacy-diagnostics.cjs
describeSettings() -> require settings.js
The suspected cause is entryCanBeWrapped in lib/optimize/ConcatenatedModule.js. It accepts NormalModule and eligible ExternalModule instances, but not another ConcatenatedModule. Lazy wrapping propagation appears to stop at the separate settings concatenation.
As a diagnostic experiment on 5.110.3, allowing ConcatenatedModule in that predicate removed the failure in a reduced reproduction. This is not proposed as a fully validated fix; the repository above uses unmodified Webpack.
This appears related to the lazy CommonJS concatenation implementation introduced in #21519, but is distinct from the name-collision and dropped-side-effect cases in #21593.
Metadata
Metadata
Assignees
Labels
Type
Fields
Priority
Activity