From f22bff9dfaf87d019d8e406a5595dbaf4fe1ac73 Mon Sep 17 00:00:00 2001 From: Alan Agius <17563226+alan-agius4@users.noreply.github.com> Date: Fri, 18 Sep 2026 15:52:23 +0000 Subject: [PATCH 1/2] fix(@angular/build): ensure chokidar watcher is ready before returning When initializing chokidar.watch with ignoreInitial: true, files visited during the initial scan are treated as the initial baseline and do not emit change events. If createChokidarWatcher returns before the initial scan completes, subsequent file modifications made shortly after setup can be visited for the first time during the initial scan, causing the change event to be dropped. Awaiting the ready event ensures the watcher's initial scan is completed and the watcher is actively listening before returning. This resolves test flakiness in rebuild error detection tests where files are modified immediately after the initial build. --- .../dev-server/tests/behavior/build-errors_spec.ts | 4 ++-- packages/angular/build/src/tools/esbuild/watcher.ts | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/angular/build/src/builders/dev-server/tests/behavior/build-errors_spec.ts b/packages/angular/build/src/builders/dev-server/tests/behavior/build-errors_spec.ts index 424c520eab2d..9608e0dc976e 100644 --- a/packages/angular/build/src/builders/dev-server/tests/behavior/build-errors_spec.ts +++ b/packages/angular/build/src/builders/dev-server/tests/behavior/build-errors_spec.ts @@ -38,8 +38,8 @@ describeServeBuilder(executeDevServer, DEV_SERVER_BUILDER_INFO, (harness, setupT expectNoLog(logs, 'Unexpected character "EOF"'); }, ], - { outputLogsOnFailure: false, timeout: 90_000 }, + { outputLogsOnFailure: false }, ); - }, 120_000); + }); }); }); diff --git a/packages/angular/build/src/tools/esbuild/watcher.ts b/packages/angular/build/src/tools/esbuild/watcher.ts index 9923f08fea7f..6995b39a1fd3 100644 --- a/packages/angular/build/src/tools/esbuild/watcher.ts +++ b/packages/angular/build/src/tools/esbuild/watcher.ts @@ -8,6 +8,7 @@ import type * as ParcelWatcher from '@parcel/watcher'; import type * as Chokidar from 'chokidar'; +import { once } from 'node:events'; import * as fs from 'node:fs'; import * as path from 'node:path'; import picomatch from 'picomatch'; @@ -663,6 +664,13 @@ async function createChokidarWatcher( usePolling: !!options?.polling, interval: options?.interval, }); + + // Wait for the watcher to complete its initial filesystem scan before returning. + // With `ignoreInitial: true`, any file visited during the initial scan is treated as the initial baseline + // and will not emit 'add' or 'change' events. Awaiting 'ready' ensures that rapid file modifications + // made right after watcher setup (e.g. in rebuild tests) are not swallowed as initial files. + await once(watcher, 'ready'); + const initTime = Date.now(); const handleEvent = (type: 'added' | 'modified' | 'removed', rawPath: string) => { From ad6a6ca2c43251c396d2ca7333e08972747919fd Mon Sep 17 00:00:00 2001 From: Alan Agius <17563226+alan-agius4@users.noreply.github.com> Date: Fri, 18 Sep 2026 16:17:36 +0000 Subject: [PATCH 2/2] fixup! fix(@angular/build): ensure chokidar watcher is ready before returning --- packages/angular/build/src/tools/esbuild/watcher.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/angular/build/src/tools/esbuild/watcher.ts b/packages/angular/build/src/tools/esbuild/watcher.ts index 6995b39a1fd3..e30909fa6813 100644 --- a/packages/angular/build/src/tools/esbuild/watcher.ts +++ b/packages/angular/build/src/tools/esbuild/watcher.ts @@ -665,14 +665,14 @@ async function createChokidarWatcher( interval: options?.interval, }); + const initTime = Date.now(); + // Wait for the watcher to complete its initial filesystem scan before returning. // With `ignoreInitial: true`, any file visited during the initial scan is treated as the initial baseline // and will not emit 'add' or 'change' events. Awaiting 'ready' ensures that rapid file modifications // made right after watcher setup (e.g. in rebuild tests) are not swallowed as initial files. await once(watcher, 'ready'); - const initTime = Date.now(); - const handleEvent = (type: 'added' | 'modified' | 'removed', rawPath: string) => { const posixPath = toPosixPathNormalized(rawPath); const lookupKey = toLookupKey(posixPath, isCaseSensitive);