Command
build
Is this a regression?
The previous version in which this bug was not present was
21
Description
Since updating to Angular 22, a production-style build (optimization on, sourceMap: true) fails during the new default chunk optimization pass:
X [ERROR] Chunk optimization failed
Multiple conflicting contents for sourcemap source <root>/libs/env/src/environment.dev.ts
The build succeeds with NG_BUILD_OPTIMIZE_CHUNKS=0, which is our current workaround.
The setup that triggers it: a fileReplacements entry whose with file lives in a different directory and imports a file that has the same basename and sits next to the replace file.
libs/env/src/environment.ts <- "replace"
libs/env/src/environment.dev.ts <- real file, imported by the replacement
apps/app1/src/env/environment.dev.ts <- "with", imports ../../../../libs/env/src/environment.dev
"fileReplacements": [
{
"replace": "libs/env/src/environment.ts",
"with": "apps/app1/src/env/environment.dev.ts"
}
]
The dev configuration also has "optimization": { "scripts": true, ... } and "sourceMap": true, and the app has more than 3 lazy chunks, so the chunk optimizer runs.
What I found
Building with NG_BUILD_OPTIMIZE_CHUNKS=0 and inspecting the emitted .js.map files shows the same source path recorded twice in one chunk, with different sourcesContent:
chunk-XXXX.js.map sources:
libs/env/src/environment.dev.ts (content of the real lib file)
libs/env/src/environment.dev.ts (content of apps/app1/src/env/environment.dev.ts!)
So the replaced module is recorded under dirname(replace) + basename(with) instead of the actual path of the with file. My understanding of the cause: the replacement happens in the compiler plugin's onLoad hook, so the esbuild module keeps the original path, and the emitted sourcemap for the compiled replacement file only carries the basename, which esbuild then resolves against the original module's directory.
This wrong path is harmless on its own, but when it collides with a real module that is also in the bundle (here: the lib's own environment.dev.ts, imported by the replacement file), the rollup-based chunk optimizer refuses to merge the sourcemaps and the whole build fails.
Minimal Reproduction
see above
Exception or Error
Application bundle generation failed.
X [ERROR] Chunk optimization failed
Multiple conflicting contents for sourcemap source <root>/libs/env/src/environment.dev.ts
Your Environment
Angular CLI / @angular/build: 22.0.9
Angular: 22.0.8
Node: 22.13.0
Package manager: npm
OS: reproduced on both Windows 11 and macOS
Anything else relevant?
Workaround: NG_BUILD_OPTIMIZE_CHUNKS=false.
Expected behavior: the sourcemap source for a replaced module should be the path of the with file (or at least a path that cannot collide with a different real module), and chunk optimization should not fail the build over it.
Two notes: the placeholder stands for the absolute workspace path from the real error output, and I kept the lazy-chunk condition in ("more than 3 lazy chunks") since the optimizer only runs above that threshold — without it their triage can't repro.
Command
build
Is this a regression?
The previous version in which this bug was not present was
21
Description
Since updating to Angular 22, a production-style build (
optimizationon,sourceMap: true) fails during the new default chunk optimization pass:The build succeeds with
NG_BUILD_OPTIMIZE_CHUNKS=0, which is our current workaround.The setup that triggers it: a
fileReplacementsentry whosewithfile lives in a different directory and imports a file that has the same basename and sits next to thereplacefile.The dev configuration also has
"optimization": { "scripts": true, ... }and"sourceMap": true, and the app has more than 3 lazy chunks, so the chunk optimizer runs.What I found
Building with
NG_BUILD_OPTIMIZE_CHUNKS=0and inspecting the emitted.js.mapfiles shows the same source path recorded twice in one chunk, with differentsourcesContent:So the replaced module is recorded under
dirname(replace) + basename(with)instead of the actual path of thewithfile. My understanding of the cause: the replacement happens in the compiler plugin'sonLoadhook, so the esbuild module keeps the original path, and the emitted sourcemap for the compiled replacement file only carries the basename, which esbuild then resolves against the original module's directory.This wrong path is harmless on its own, but when it collides with a real module that is also in the bundle (here: the lib's own
environment.dev.ts, imported by the replacement file), the rollup-based chunk optimizer refuses to merge the sourcemaps and the whole build fails.Minimal Reproduction
see above
Exception or Error
Your Environment
Anything else relevant?
Workaround:
NG_BUILD_OPTIMIZE_CHUNKS=false.Expected behavior: the sourcemap source for a replaced module should be the path of the
withfile (or at least a path that cannot collide with a different real module), and chunk optimization should not fail the build over it.Two notes: the placeholder stands for the absolute workspace path from the real error output, and I kept the lazy-chunk condition in ("more than 3 lazy chunks") since the optimizer only runs above that threshold — without it their triage can't repro.