Skip to content

CommonJS concatenation eagerly evaluates a separate concatenation before application initialization #22221

Closed

Description

@mackwang112

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

Repository: https://github.com/mackwang112/webpack-demo2

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 start

Compilation 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: optimizationTree-shaking, splitChunks, concatenation, ids (lib/optimize, lib/ids)

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions