Skip to content

DllReferencePlugin reuses stale buildMeta and exports after manifest changes #22027

Closed

Description

@hardfist

Have you used AI?

Yes. AI assisted with preparing the reproduction and report. The reproduction was executed locally against the npm release, including a clean npm ci installation.

Which part of webpack is affected?

cache (persistent/filesystem cache); memory cache is also affected.

Bug Description

When a DLL module changes from ESM to CommonJS while its module id and DLL name stay the same, DllPlugin generates a manifest with updated exports and buildMeta. With caching enabled, the consuming compilation reads the new manifest but reuses the old DelegatedModule build result, producing different runtime output from a build with caching disabled.

Link to Minimal Reproduction and steps to reproduce

Reproduction repository — webpack 5.110.3 is pinned with a lockfile.

git clone https://github.com/hardfist/webpack-dll-manifest-cache-repro.git
cd webpack-dll-manifest-cache-repro
npm ci
npm run repro

The script builds a real DLL from:

export const old = "dll-old";
export default "dll-default";

It builds a consumer that imports the default export through DllReferencePlugin, then rebuilds the DLL from:

module.exports = { default: "dll-default", added: "dll-added" };

The plugin uses a manifest file path. Both manifests are generated by DllPlugin; the DLL name and named module id (./item.js) stay unchanged. Each consumer bundle runs in a fresh Node process and loads the actual rebuilt DLL.

The script compares:

  • cache: false: a new compiler for each build.
  • Memory cache: two compiler.run() calls on the same compiler, purging the manifest's input filesystem entry and marking it modified before the second build.
  • Filesystem cache: close the first compiler to flush the cache, then create a new compiler using the same cache directory.

Exit code 1 with BUG REPRODUCED is expected on the affected version. Detailed results are written to .work/results.json.

Expected Behavior

All second builds should match the cache-disabled control and export:

{"default":"dll-default","added":"dll-added"}

Actual Behavior

In every second build, the new manifest has buildMeta.exportsType: "default" and exports ["added", "default"].

Consumer cache Delegated module built / reused Module buildMeta.exportsType Consumer default export
Disabled 1 / 0 default {"default":"dll-default","added":"dll-added"}
Memory 0 / 1 namespace "dll-default"
Filesystem 0 / 1 namespace "dll-default"

Both cached compilations also retain provided exports ["default", "old"].

The reproduction asserts that the manifest is in compilation.fileDependencies and the module's delegateData matches the new manifest entry. The identifier remains unchanged.

Environment

OS: macOS 26.5.2 (arm64)
Node.js: 24.16.0
npm: 10.9.8
webpack: 5.110.3 (npm latest at the time of reporting)
API: Node.js API; webpack-cli is not used

Is this a regression?

Unknown; earlier versions have not been tested.

Additional Context

In DelegatedModule, updateCacheModule() replaces delegateData, but retains the old buildMeta and dependencies. needBuild() returns !this.buildMeta, so an existing build result prevents rebuilding. build() is where the manifest's BuildMeta is copied and the StaticExportsDependency is recreated.

Activity

  1. added
    area: cachePersistent and memory caching, serialization (lib/cache, lib/serialization)
    on Sep 10, 2026
  2. self-assigned this
    on Sep 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area: cachePersistent and memory caching, serialization (lib/cache, lib/serialization)

Type

No type

Fields

Priority

None yet

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions