From 35cd31cd626bf4ac8080f2dca39b6fa8c6223aa0 Mon Sep 17 00:00:00 2001 From: Charles Lyding <19598772+clydin@users.noreply.github.com> Date: Mon, 5 May 2025 14:11:23 -0400 Subject: [PATCH] fix(@angular/build): avoid attempting to watch bundler internal files The internal bundler (`esbuild`) may use virtual files to represent added content to the output files. This includes names such as `` and `` to reference object define values and runtime code, respectively. It may also use paths that end with `(disabled):` where `` is one of Node.js' builtin modules. All these cases are now handled by the file watching system and will not be attempted to be watched. Previously these virtual files would be considered removed by the file watching system as they do not actually exist on the file system. --- .../src/tools/esbuild/bundler-context.ts | 36 ++++++++++++++----- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/packages/angular/build/src/tools/esbuild/bundler-context.ts b/packages/angular/build/src/tools/esbuild/bundler-context.ts index 0d6b3d59ceaf..9b722e41640e 100644 --- a/packages/angular/build/src/tools/esbuild/bundler-context.ts +++ b/packages/angular/build/src/tools/esbuild/bundler-context.ts @@ -18,6 +18,7 @@ import { context, } from 'esbuild'; import assert from 'node:assert'; +import { builtinModules } from 'node:module'; import { basename, extname, join, relative } from 'node:path'; import { LoadResultCache, MemoryLoadResultCache } from './load-result-cache'; import { SERVER_GENERATED_EXTERNALS, convertOutputFile } from './utils'; @@ -245,7 +246,7 @@ export class BundlerContext { // When incremental always add any files from the load result cache if (this.#loadCache) { for (const file of this.#loadCache.watchFiles) { - if (!isInternalAngularFileOrEsBuildDefine(file)) { + if (!isInternalAngularFile(file)) { // watch files are fully resolved paths this.watchFiles.add(file); } @@ -260,10 +261,12 @@ export class BundlerContext { if (this.incremental) { // Add input files except virtual angular files which do not exist on disk for (const input of Object.keys(result.metafile.inputs)) { - if (!isInternalAngularFileOrEsBuildDefine(input)) { - // input file paths are always relative to the workspace root - this.watchFiles.add(join(this.workspaceRoot, input)); + if (isInternalAngularFile(input) || isInternalBundlerFile(input)) { + continue; } + + // Input file paths are always relative to the workspace root + this.watchFiles.add(join(this.workspaceRoot, input)); } } @@ -417,12 +420,12 @@ export class BundlerContext { #addErrorsToWatch(result: BuildFailure | BuildResult): void { for (const error of result.errors) { let file = error.location?.file; - if (file && !isInternalAngularFileOrEsBuildDefine(file)) { + if (file && !isInternalAngularFile(file)) { this.watchFiles.add(join(this.workspaceRoot, file)); } for (const note of error.notes) { file = note.location?.file; - if (file && !isInternalAngularFileOrEsBuildDefine(file)) { + if (file && !isInternalAngularFile(file)) { this.watchFiles.add(join(this.workspaceRoot, file)); } } @@ -475,6 +478,23 @@ export class BundlerContext { } } -function isInternalAngularFileOrEsBuildDefine(file: string) { - return file.startsWith('angular:') || file.startsWith('" or "" + if (file[0] === '<' && file.at(-1) === '>') { + return true; + } + + const DISABLED_BUILTIN = '(disabled):'; + + // Disabled node builtins such as "/some/path/(disabled):fs" + const disabledIndex = file.indexOf(DISABLED_BUILTIN); + if (disabledIndex >= 0) { + return builtinModules.includes(file.slice(disabledIndex + DISABLED_BUILTIN.length)); + } + + return false; }