You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@angular-architects/native-federation-v4 imports esbuild without declaring it as a dependency (the same class of problem as the mrmime gap in #100). The import resolves through the package manager's hoisting to whatever esbuild happens to sit at the workspace root, which is not necessarily the version @angular/build uses internally.
When the two versions differ, the federation build's error handling can break: an esbuild build that fails does not surface its failure. Instead, every attempt ends in
✘ [ERROR] Unexpected end of JSON input [plugin onEnd]
<ws>/node_modules/esbuild/lib/main.js:1882:14:
1882 │ return JSON.parse(text);
at requestCallbacks.on-end (<ws>/node_modules/esbuild/lib/main.js:1216:54)
and the build is retried in a hot loop, roughly 8 times per second, forever. The compile errors are printed on every iteration (we killed one run after 10 minutes with the same two TS errors printed 13,467 times into a 16 MB log), the process never exits, and CI has to be aborted by hand.
With matched versions, the identical failure exits code 1 within seconds with ERRR Error building federation artifacts, so only the mismatch separates "clean failure" from "infinite loop".
A correction to an earlier version of this report: not every mismatched pair breaks. Re-measured on the same tree with a deliberate compile error, 0.27.4 against 0.27.3 loops (409 JSON crashes in 75s) while 0.28.1 against 0.27.3 exits 1 normally. A mismatch appears to be necessary but not sufficient, presumably depending on whether the two versions' service protocol differs. Since which pairs are safe is not predictable from the outside, treating any mismatch as a defect still seems right, but the declared dependency is what removes the whole class.
Affected versions
@angular-architects/native-federation-v4@21.2.8 (dist-tag latest): esbuild absent from dependencies, imported in src/builders/build/builder.js and src/utils/angular-esbuild-adapter.js
@angular-architects/native-federation-v4@20.4.3 (dist-tag v20-support): same
Observed against @angular/build 21.2.15 (esbuild 0.27.3) and 21.2.19 (esbuild 0.28.1), Node 24, macOS.
git clone https://github.com/Ionaru/nf-v4-esbuild-mismatch-repro
cd nf-v4-esbuild-mismatch-repro && npm install
npx ng build --configuration production # never exits
It is a stock ng new plus native-federation-v4:init, one type error in the exposed component,
and esbuild pinned away from the version @angular/build uses. To confirm the cause in place:
npm i -D esbuild@0.28.1 --save-exact # match @angular/build
npx ng build --configuration production # exits 1 in ~1s with ERRR Error building federation artifacts
bridge resolves
@angular/build resolves
failing build
0.27.4
0.28.1
hangs, JSON-crash storm
0.28.1
0.28.1
exits 1 in ~1s
The pin only makes it deterministic. npm ls esbuild in that repo shows @angular/build -> vite -> esbuild@0.27.4 next to esbuild@0.28.1, so hoisting can produce the same split on its own as
unrelated dependencies shift; under pnpm the undeclared import falls back to .pnpm/node_modules.
Diagnosis one-liner for any workspace, both lines must match:
In a large pnpm workspace where the bridge resolved 0.27.4 (pnpm's hoisted fallback) and @angular/build resolved 0.27.3, we flipped only the hoisted node_modules/.pnpm/node_modules/esbuild symlink back and forth, with an intentionally failing build:
bridge esbuild
@angular/build esbuild
failing build
0.27.4
0.27.3
hangs, JSON-crash storm
0.27.3
0.27.3
exits 1 in ~60s, ERRR Error building federation artifacts
0.27.4
0.27.3
hangs again
Same tree, same code, same config in all three runs.
This is easy to hit in real projects: under npm/yarn hoisting any dependency that drags in another esbuild at the root changes what the bridge gets, and under pnpm the undeclared import falls back to .pnpm/node_modules, whose esbuild version shifts as unrelated dependencies change. It is invisible while builds succeed and turns the first real compile error into a CI hang.
Defence in depth: since the adapter passes @angular/build's compiler plugin into a context created by its own esbuild import, resolving that esbuild instance from @angular/build (or asserting the versions match at startup) would fail fast with a clear message instead of looping.
Possibly related: #95 reports a build that does not exit after a successful run on the mainline package; if that setup also carries two esbuild versions, it may be the same mechanism.
Summary
@angular-architects/native-federation-v4importsesbuildwithout declaring it as a dependency (the same class of problem as themrmimegap in #100). The import resolves through the package manager's hoisting to whatever esbuild happens to sit at the workspace root, which is not necessarily the version@angular/builduses internally.When the two versions differ, the federation build's error handling can break: an esbuild build that fails does not surface its failure. Instead, every attempt ends in
and the build is retried in a hot loop, roughly 8 times per second, forever. The compile errors are printed on every iteration (we killed one run after 10 minutes with the same two TS errors printed 13,467 times into a 16 MB log), the process never exits, and CI has to be aborted by hand.
With matched versions, the identical failure exits code 1 within seconds with
ERRR Error building federation artifacts, so only the mismatch separates "clean failure" from "infinite loop".A correction to an earlier version of this report: not every mismatched pair breaks. Re-measured on the same tree with a deliberate compile error,
0.27.4against0.27.3loops (409 JSON crashes in 75s) while0.28.1against0.27.3exits 1 normally. A mismatch appears to be necessary but not sufficient, presumably depending on whether the two versions' service protocol differs. Since which pairs are safe is not predictable from the outside, treating any mismatch as a defect still seems right, but the declared dependency is what removes the whole class.Affected versions
@angular-architects/native-federation-v4@21.2.8(dist-taglatest):esbuildabsent fromdependencies, imported insrc/builders/build/builder.jsandsrc/utils/angular-esbuild-adapter.js@angular-architects/native-federation-v4@20.4.3(dist-tagv20-support): sameObserved against
@angular/build21.2.15 (esbuild 0.27.3) and 21.2.19 (esbuild 0.28.1), Node 24, macOS.Minimal reproduction
https://github.com/Ionaru/nf-v4-esbuild-mismatch-repro
It is a stock
ng newplusnative-federation-v4:init, one type error in the exposed component,and
esbuildpinned away from the version@angular/builduses. To confirm the cause in place:@angular/buildresolvesThe pin only makes it deterministic.
npm ls esbuildin that repo shows@angular/build -> vite -> esbuild@0.27.4next toesbuild@0.28.1, so hoisting can produce the same split on its own asunrelated dependencies shift; under pnpm the undeclared import falls back to
.pnpm/node_modules.Diagnosis one-liner for any workspace, both lines must match:
Isolation evidence in a real workspace
In a large pnpm workspace where the bridge resolved 0.27.4 (pnpm's hoisted fallback) and
@angular/buildresolved 0.27.3, we flipped only the hoistednode_modules/.pnpm/node_modules/esbuildsymlink back and forth, with an intentionally failing build:ERRR Error building federation artifactsSame tree, same code, same config in all three runs.
This is easy to hit in real projects: under npm/yarn hoisting any dependency that drags in another esbuild at the root changes what the bridge gets, and under pnpm the undeclared import falls back to
.pnpm/node_modules, whose esbuild version shifts as unrelated dependencies change. It is invisible while builds succeed and turns the first real compile error into a CI hang.Suggested fix
esbuildindependencies, aligned with the@angular/buildversion in use. The mainline@angular-architects/native-federationalready declares it, so this is drift in the bridge, like native-federation-v4 imports mrmime without declaring it as a dependency #100.@angular/build's compiler plugin into a context created by its own esbuild import, resolving that esbuild instance from@angular/build(or asserting the versions match at startup) would fail fast with a clear message instead of looping.Possibly related: #95 reports a build that does not exit after a successful run on the mainline package; if that setup also carries two esbuild versions, it may be the same mechanism.