Skip to content

unit-test builder OOMs on large suites since 22.1.5: disableCodeSplitting duplicates shared code across every spec entry point #33969

Description

@mokbkalvad

Command

test

Is this a regression?

  • Yes, this behavior used to work in the previous version

The previous version in which this bug was not present was

No response

Description

Since @angular/build 22.1.5, ng test --no-watch no longer completes on our suite. The build
phase — before any vitest worker spawns — grows a single native esbuild process past 5 GB and
hard-freezes a 16 GB machine. It never reaches the test run. No application code changed; only
the builder version.

The cause is disableCodeSplitting: true, added in 22.1.5 to
src/builders/unit-test/runners/vitest/build-options.js, which sets splitting = false and
ignoreAnnotations = true in src/tools/esbuild/application-code-bundle.js.

Because every spec file is its own entry point, disabling splitting makes each bundle inline its
own private copy of all shared first-party code, and ignoreAnnotations additionally prevents
sideEffects: false pruning. Memory scales linearly with spec count times shared-graph size.

I understand this fixes a real correctness bug (shared-chunk exports read as undefined via live
ESM bindings). The problem is that the trade-off is unconditional, and on a suite of a few hundred
specs the memory cost is not survivable.

Measurements — peak RSS of the esbuild process on 22.1.6, same tree, --include used to vary
only the number of entry points:

spec files peak RSS
25 387 MB
50 723 MB
100 1251 MB
200 2273 MB

Linear at ~10.8 MB per spec file, projecting ~4.8 GB for our full 431 specs.

Projects using barrel files (index.ts re-exports) are hit far harder, since ignoreAnnotations
prevents pruning the unused members a barrel drags in. Same suite and builder, before removing
barrel imports: 25 specs → 2025 MB (vs 387 MB), 50 specs → over 3089 MB (vs 723 MB). That is
~80 MB per spec, projecting ~30 GB, which is why the failure presents as an instant machine freeze
rather than a slow build.

Patching disableCodeSplitting back to false on 22.1.6 runs the full suite at 772 MB peak with
all 431 files / 3523 tests passing
, confirming this flag is the sole cause.

Suggested resolution — either expose disableCodeSplitting as a unit-test builder option so
projects can opt back into splitting (it is currently hardcoded and absent from both schema.json
files, so there is no supported workaround short of patching the package), or address the
live-binding undefined issue without disabling splitting wholesale, e.g. by forcing eager
initialization of shared chunks.

Minimal Reproduction

Any project using @angular/build:unit-test with the vitest runner and a few hundred spec files
sharing a common set of services/components. Run ng test --no-watch on 22.1.4, then on 22.1.5+,
watching the RSS of the esbuild process during "Building...".

Exception or Error


Your Environment

Angular CLI       : 22.1.6
Angular           : 22.1.4
Node.js           : 26.5.1
Package Manager   : pnpm 11.21.0

Anything else relevant?

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions