Which @angular/* package(s) are the source of the bug?
Don't known / other
Is this a regression?
No
Description
With NG_BUILD_OPTIMIZE_CHUNKS=1 set and a production build (optimization: true), we have a case where the minifier assigns the same short identifier to two completely unrelated top-level functions that end up merged into the same scope, and the second silently overwrites the first — with no error thrown, no console output, nothing. The app just hangs on its initial loading screen forever, because one of the two colliding functions is an internal Angular Router helper used during initial navigation, and it silently gets replaced by an unrelated third-party function with an incompatible signature.
Removing the same NG_BUILD_OPTIMIZE_CHUNKS=1 flag (falling back to ng build --configuration production without it), or removing the third-party plugin's usage entirely, or building with --configuration development (optimization: false), all avoid the issue. Only the specific combination of optimization: true + NG_BUILD_OPTIMIZE_CHUNKS=1 + this exact bundle composition reproduces it.
Please provide a link to a minimal reproduction of the bug
No response
Please provide the exception or error you saw
In the resulting `main-*.js`, we found two distinct `function Do(...)` declarations landing in the same scope:
// Appears to be an internal Angular Router helper (route-children matching)
function Do(t,n,e){return n.length===0&&!t.children[e]}
// This is dayjs's `isBetween` plugin (dayjs/plugin/isBetween), matching its real
// implementation signature (two date args, each normalized, then compared)
function Do(e,t,n,a){var r=ee(e)?e:Y(e),i=ee(t)?t:Y(t)...
Since both are plain `function` declarations sharing the identifier `Do` in what appears to be the same merged scope, the second declaration overwrites the first (standard JS function-hoisting/redeclaration behavior — no error). Anywhere the Router's own code invokes its internal `Do(...)`, it now silently calls the third-party `isBetween` implementation instead, with mismatched arguments, silently producing wrong/undefined results instead of throwing. This corrupts route resolution during the app's initial navigation with **no thrown exception, no unhandled promise rejection, and no `NavigationError`** — the app just never finishes bootstrapping past its static loading screen.
Please provide the environment you discovered this bug in (run ng version)
- `@angular/cli`: 21.2.7
- `@angular/build`: 21.2.7
- esbuild (resolved): 0.25.12
- Node: v22.22.3
- Build command: `NG_BUILD_OPTIMIZE_CHUNKS=1 ng build --configuration production`
Anything else?
We do not yet have a minimal, isolated reproduction — the collision appears to depend on the exact set of identifiers competing for short names across the entire production bundle, so a small standalone repro may not trigger the same collision at all. We can confirm, in our own (large, multi-hundred-component) application:
ng build --configuration development → works.
ng build --configuration production (no NG_BUILD_OPTIMIZE_CHUNKS) → works.
NG_BUILD_OPTIMIZE_CHUNKS=1 ng build --configuration production → app hangs indefinitely on its initial loading screen, no console errors of any kind.
- Removing our usage of
dayjs/plugin/isBetween (or moving it behind a dynamic import() so it lands in its own chunk instead of being merged into the main chunk) resolves the hang under the same build command.
Happy to work with a maintainer to narrow this further (e.g. sharing an anonymized/minified diff, or a source-mapped stack once we can get one) if a minimal repro turns out to be infeasible to construct by hand.
Expected behavior
Minification should never allow two semantically distinct, unrelated functions to silently collide under the same identifier within a merged scope — at minimum, this class of chunk-merging optimization should not be able to produce a naming collision between framework-internal code and application/library code. Ideally: (a) this shouldn't be possible in the first place, and (b) if a code path fails due to this class of corruption, it should fail loudly (a thrown error) rather than silently returning wrong values.
Additional notes
- This may be specific to the experimental
NG_BUILD_OPTIMIZE_CHUNKS flag's chunk-merging behavior, since removing only that env var (keeping --configuration production) avoids the bug in our case.
- We only noticed this because we happened to grep the built output for a known library function name (
isBetween) after observing the symptom (silent hang, zero console output) and ruling out several other causes over an extended debugging session (browserslist config, dayjs version mismatches, module evaluation order/timing). The root cause was ultimately unrelated to all of those — purely a minifier identifier collision.
Which @angular/* package(s) are the source of the bug?
Don't known / other
Is this a regression?
No
Description
With
NG_BUILD_OPTIMIZE_CHUNKS=1set and a production build (optimization: true), we have a case where the minifier assigns the same short identifier to two completely unrelated top-level functions that end up merged into the same scope, and the second silently overwrites the first — with no error thrown, no console output, nothing. The app just hangs on its initial loading screen forever, because one of the two colliding functions is an internal Angular Router helper used during initial navigation, and it silently gets replaced by an unrelated third-party function with an incompatible signature.Removing the same
NG_BUILD_OPTIMIZE_CHUNKS=1flag (falling back tong build --configuration productionwithout it), or removing the third-party plugin's usage entirely, or building with--configuration development(optimization: false), all avoid the issue. Only the specific combination ofoptimization: true+NG_BUILD_OPTIMIZE_CHUNKS=1+ this exact bundle composition reproduces it.Please provide a link to a minimal reproduction of the bug
No response
Please provide the exception or error you saw
Please provide the environment you discovered this bug in (run
ng version)Anything else?
We do not yet have a minimal, isolated reproduction — the collision appears to depend on the exact set of identifiers competing for short names across the entire production bundle, so a small standalone repro may not trigger the same collision at all. We can confirm, in our own (large, multi-hundred-component) application:
ng build --configuration development→ works.ng build --configuration production(noNG_BUILD_OPTIMIZE_CHUNKS) → works.NG_BUILD_OPTIMIZE_CHUNKS=1 ng build --configuration production→ app hangs indefinitely on its initial loading screen, no console errors of any kind.dayjs/plugin/isBetween(or moving it behind a dynamicimport()so it lands in its own chunk instead of being merged into the main chunk) resolves the hang under the same build command.Happy to work with a maintainer to narrow this further (e.g. sharing an anonymized/minified diff, or a source-mapped stack once we can get one) if a minimal repro turns out to be infeasible to construct by hand.
Expected behavior
Minification should never allow two semantically distinct, unrelated functions to silently collide under the same identifier within a merged scope — at minimum, this class of chunk-merging optimization should not be able to produce a naming collision between framework-internal code and application/library code. Ideally: (a) this shouldn't be possible in the first place, and (b) if a code path fails due to this class of corruption, it should fail loudly (a thrown error) rather than silently returning wrong values.
Additional notes
NG_BUILD_OPTIMIZE_CHUNKSflag's chunk-merging behavior, since removing only that env var (keeping--configuration production) avoids the bug in our case.isBetween) after observing the symptom (silent hang, zero console output) and ruling out several other causes over an extended debugging session (browserslist config, dayjs version mismatches, module evaluation order/timing). The root cause was ultimately unrelated to all of those — purely a minifier identifier collision.