Repository navigation
Watch mode (ng test, ng serve) stalls after "Watch mode enabled" when preserveSymlinks: true — chokidar scans entire workspace incl. node_modules (regression in 22.2.0) #34162
Description
Activity
- addedgemini-triagedLabel noting that an issue has been triaged by geminiLabel noting that an issue has been triaged by gemini
on Sep 24, 2026 - changed the title
[-]`ng test` (watch mode) stalls after "Watch mode enabled" when `preserveSymlinks: true` — chokidar scans entire workspace incl. `node_modules` (regression in 22.2.0)[/-][+]Watch mode (`ng test`, 'ng serve') stalls after "Watch mode enabled" when `preserveSymlinks: true` — chokidar scans entire workspace incl. `node_modules` (regression in 22.2.0)[/+]on Sep 24, 2026 - changed the title
[-]Watch mode (`ng test`, 'ng serve') stalls after "Watch mode enabled" when `preserveSymlinks: true` — chokidar scans entire workspace incl. `node_modules` (regression in 22.2.0)[/-][+]Watch mode (`ng test`, `ng serve`) stalls after "Watch mode enabled" when `preserveSymlinks: true` — chokidar scans entire workspace incl. `node_modules` (regression in 22.2.0)[/+]on Sep 24, 2026 Follow-up:
ng serveis affected as well, not justng test.The dev server only calls
server.listen()(src/builders/dev-server/vite/index.js) after it receives the first result frombuildApplicationInternal. That result is emitted only aftersetupWatcher()returns, which waits for chokidar's initial scan of the workspace root. Until then the terminal shows "Watch mode enabled. Watching for file changes..." and nothing is listening on the port, so the browser can't connect.Measured with the same fresh reproduction project (
ng serve --port 4300, Windows 11), time from "Watch mode enabled" until the port answers HTTP:preserveSymlinksPort answers after false1 s true61 s (The scan time varies between runs — 61 s here vs. ~2 min for the earlier
ng testmeasurement on the same project — presumably due to file system caching, but it is consistently far above thefalsebaseline.)We first noticed this in our large application workspace (≈106 000 files in
node_modules), where the dev server stayed unreachable after "Watch mode enabled".ng build --watchpresumably behaves the same, since it goes through the samerunEsBuildBuildAction/setupWatcherpath (not measured).Workaround we use for now: a build configuration
"no-preserve-symlinks": { "preserveSymlinks": false }appended to the dev-server--build-target, dropped only when serving with annpm link-ed library (accepting the slow start in that case).Reacted by xidedix- added 6 commits that reference this issue
on Sep 24, 2026 - added a commit that references this issue
on Sep 25, 2026 - added a commit that references this issue
on Sep 25, 2026 - marked ng build --watch --preserve-symlinks dies with EMFILE when watch mode starts #34185 as a duplicate of this issue
on Sep 27, 2026
Command
Is this a regression?
The previous version in which this bug was not present was
22.1.8
Description
(AI generated)
After updating
@angular/build/@angular/clifrom 22.1.8 to 22.2.0,ng testin watch mode (@angular/build:unit-test, Vitest runner) no longer starts running tests after the initial build when the build target has"preserveSymlinks": true. The output stops at:With
NG_TEST_LOG=1,VitestExecutornever logsExecuting test run— the first build result is not handed to the executor. We stopped the process after more than 4 minutes of waiting.ng test --watch=falseis unaffected (the full suite finishes in ~3 min including the build).Setting
"preserveSymlinks": falsein the test build configuration makes Vitest start ~2 s after the build completes. Downgrading Vitest (5.0.1 → 4.1.11) makes no difference, so it is not Vitest-related.Cause (from reading
@angular/build22.2.0 sources):In
src/builders/application/build-action.js, watch mode now callssetupWatcher()fromsrc/utils/watcher.jsbefore emitting the first build result:createWatcher()uses chokidar instead of@parcel/watcherwheneverfollowSymlinks(=preserveSymlinks) is true.createChokidarWatcher()callschokidar.watch(rootDir, …)on the entire workspace root (cwd: workspaceRoot), regardless ofNG_BUILD_WATCH_ROOT.**/node_modules/**ignore pattern is only added whenshouldWatchRoot && !preserveSymlinks, so withpreserveSymlinks: truechokidar also walks all ofnode_modules(≈106 000 files in our workspace).await once(watcher, 'ready')(added in fix(@angular/build): ensure chokidar watcher is ready before returning #34122), i.e. it blocks until that full initial scan has completed — withfollowSymlinks: trueon Windows.In 22.1.8,
build-action.jsusedtools/esbuild/watcher.jsand only watched the files/directories fromresult.watchFiles(plus the project root only ifNG_BUILD_WATCH_ROOTwas set), with no wait for an initial scan, so startup was instant.preserveSymlinks: trueis a common setting for workspaces thatnpm linklocal libraries, so this effectively makes watch mode unusable for them.Since
ng serveandng build --watchgo through the samerunEsBuildBuildAction/setupWatcherpath, they are affected as well, see comment below.Possible fixes:
watchFiles(and the root only withNG_BUILD_WATCH_ROOT), as in 22.1.8, for the chokidar path; ornode_modulesunder the workspace root in the chokidar path as well — explicitly added watch files outside the root (e.g.npm linktargets) would still be watched; orreadywhen the initial scan is large.Minimal Reproduction
Verified by me (human): https://github.com/reifi/angular-preserve-symlinks-watch-repro
Reproducible with a freshly generated project:
npx @angular/cli@22.2.0 new repro --defaults --test-runner=vitestangular.json, add"preserveSymlinks": truetoprojects.repro.architect.build.options.ng test(watch mode;ng test --watchin a non-TTY shell).Measured on Windows 11, fresh project (≈18 000 files in
node_modules), time from "Watch mode enabled" to the Vitest test summary:preserveSymlinkstrueThe delay scales with the number of files under the workspace root. In a large application workspace (≈106 000 files in
node_modules) tests had still not started after more than 4 minutes, when we stopped the process.ng test --watch=falseis not affected.Exception or Error
Your Environment
Anything else relevant?
Workaround: override
"preserveSymlinks": falsein the build configuration referenced by the unit-testbuildTarget, e.g.The unit-test builder reads
preserveSymlinksfrom the build target options (unit-test/options.js), so this only affects tests;ng serve/ng buildkeeppreserveSymlinks: truefornpm link.