diff --git a/packages/angular_devkit/build_angular/src/builders/browser-esbuild/angular/angular-compilation.ts b/packages/angular_devkit/build_angular/src/builders/browser-esbuild/angular/angular-compilation.ts index de1e775dbdd8..1889cdfebe6a 100644 --- a/packages/angular_devkit/build_angular/src/builders/browser-esbuild/angular/angular-compilation.ts +++ b/packages/angular_devkit/build_angular/src/builders/browser-esbuild/angular/angular-compilation.ts @@ -53,7 +53,11 @@ export abstract class AngularCompilation { tsconfig: string, hostOptions: AngularHostOptions, compilerOptionsTransformer?: (compilerOptions: ng.CompilerOptions) => ng.CompilerOptions, - ): Promise<{ affectedFiles: ReadonlySet; compilerOptions: ng.CompilerOptions }>; + ): Promise<{ + affectedFiles: ReadonlySet; + compilerOptions: ng.CompilerOptions; + referencedFiles: readonly string[]; + }>; abstract collectDiagnostics(): Iterable; diff --git a/packages/angular_devkit/build_angular/src/builders/browser-esbuild/angular/aot-compilation.ts b/packages/angular_devkit/build_angular/src/builders/browser-esbuild/angular/aot-compilation.ts index 81c60707b746..84a59e40a7b9 100644 --- a/packages/angular_devkit/build_angular/src/builders/browser-esbuild/angular/aot-compilation.ts +++ b/packages/angular_devkit/build_angular/src/builders/browser-esbuild/angular/aot-compilation.ts @@ -43,7 +43,11 @@ export class AotCompilation extends AngularCompilation { tsconfig: string, hostOptions: AngularHostOptions, compilerOptionsTransformer?: (compilerOptions: ng.CompilerOptions) => ng.CompilerOptions, - ): Promise<{ affectedFiles: ReadonlySet; compilerOptions: ng.CompilerOptions }> { + ): Promise<{ + affectedFiles: ReadonlySet; + compilerOptions: ng.CompilerOptions; + referencedFiles: readonly string[]; + }> { // Dynamically load the Angular compiler CLI package const { NgtscProgram, OptimizeFor } = await AngularCompilation.loadCompilerCli(); @@ -96,7 +100,12 @@ export class AotCompilation extends AngularCompilation { this.#state?.diagnosticCache, ); - return { affectedFiles, compilerOptions }; + const referencedFiles = typeScriptProgram + .getSourceFiles() + .filter((sourceFile) => !angularCompiler.ignoreForEmit.has(sourceFile)) + .map((sourceFile) => sourceFile.fileName); + + return { affectedFiles, compilerOptions, referencedFiles }; } *collectDiagnostics(): Iterable { diff --git a/packages/angular_devkit/build_angular/src/builders/browser-esbuild/angular/compiler-plugin.ts b/packages/angular_devkit/build_angular/src/builders/browser-esbuild/angular/compiler-plugin.ts index 0ee89d2c5cd8..4d1107398b81 100644 --- a/packages/angular_devkit/build_angular/src/builders/browser-esbuild/angular/compiler-plugin.ts +++ b/packages/angular_devkit/build_angular/src/builders/browser-esbuild/angular/compiler-plugin.ts @@ -45,6 +45,8 @@ export class SourceFileCache extends Map { readonly typeScriptFileCache = new Map(); readonly loadResultCache = new MemoryLoadResultCache(); + referencedFiles?: readonly string[]; + constructor(readonly persistentCachePath?: string) { super(); } @@ -185,6 +187,7 @@ export function createCompilerPlugin( // In watch mode, previous build state will be reused. const { compilerOptions: { allowJs }, + referencedFiles, } = await compilation.initialize(tsconfigPath, hostOptions, (compilerOptions) => { if ( compilerOptions.target === undefined || @@ -254,6 +257,11 @@ export function createCompilerPlugin( } }); + // Store referenced files for updated file watching if enabled + if (pluginOptions.sourceFileCache) { + pluginOptions.sourceFileCache.referencedFiles = referencedFiles; + } + // Reset the setup warnings so that they are only shown during the first build. setupWarnings = undefined; diff --git a/packages/angular_devkit/build_angular/src/builders/browser-esbuild/angular/jit-compilation.ts b/packages/angular_devkit/build_angular/src/builders/browser-esbuild/angular/jit-compilation.ts index 2df13e01955f..d7c455645c15 100644 --- a/packages/angular_devkit/build_angular/src/builders/browser-esbuild/angular/jit-compilation.ts +++ b/packages/angular_devkit/build_angular/src/builders/browser-esbuild/angular/jit-compilation.ts @@ -30,7 +30,11 @@ export class JitCompilation extends AngularCompilation { tsconfig: string, hostOptions: AngularHostOptions, compilerOptionsTransformer?: (compilerOptions: ng.CompilerOptions) => ng.CompilerOptions, - ): Promise<{ affectedFiles: ReadonlySet; compilerOptions: ng.CompilerOptions }> { + ): Promise<{ + affectedFiles: ReadonlySet; + compilerOptions: ng.CompilerOptions; + referencedFiles: readonly string[]; + }> { // Dynamically load the Angular compiler CLI package const { constructorParametersDownlevelTransform } = await AngularCompilation.loadCompilerCli(); @@ -68,7 +72,11 @@ export class JitCompilation extends AngularCompilation { createJitResourceTransformer(() => typeScriptProgram.getProgram().getTypeChecker()), ); - return { affectedFiles, compilerOptions }; + const referencedFiles = typeScriptProgram + .getSourceFiles() + .map((sourceFile) => sourceFile.fileName); + + return { affectedFiles, compilerOptions, referencedFiles }; } *collectDiagnostics(): Iterable { diff --git a/packages/angular_devkit/build_angular/src/builders/browser-esbuild/index.ts b/packages/angular_devkit/build_angular/src/builders/browser-esbuild/index.ts index be61739b076a..950883ab0330 100644 --- a/packages/angular_devkit/build_angular/src/builders/browser-esbuild/index.ts +++ b/packages/angular_devkit/build_angular/src/builders/browser-esbuild/index.ts @@ -73,6 +73,10 @@ class ExecutionResult { }; } + get watchFiles() { + return this.codeBundleCache?.referencedFiles ?? []; + } + createRebuildState(fileChanges: ChangedFiles): RebuildState { this.codeBundleCache?.invalidate([...fileChanges.modified, ...fileChanges.removed]); @@ -676,6 +680,10 @@ export async function* buildEsbuildBrowserInternal( ]; watcher.add(packageWatchFiles.map((file) => path.join(normalizedOptions.workspaceRoot, file))); + // Watch locations provided by the initial build result + let previousWatchFiles = new Set(result.watchFiles); + watcher.add(result.watchFiles); + // Wait for changes and rebuild as needed try { for await (const changes of watcher) { @@ -687,6 +695,14 @@ export async function* buildEsbuildBrowserInternal( execute(normalizedOptions, context, result.createRebuildState(changes)), ); + // Update watched locations provided by the new build result. + // Add any new locations + watcher.add(result.watchFiles.filter((watchFile) => !previousWatchFiles.has(watchFile))); + const newWatchFiles = new Set(result.watchFiles); + // Remove any old locations + watcher.remove([...previousWatchFiles].filter((watchFile) => !newWatchFiles.has(watchFile))); + previousWatchFiles = newWatchFiles; + if (shouldWriteResult) { // Write output files await writeResultFiles(result.outputFiles, result.assetFiles, normalizedOptions.outputPath); diff --git a/packages/angular_devkit/build_angular/src/builders/browser-esbuild/watcher.ts b/packages/angular_devkit/build_angular/src/builders/browser-esbuild/watcher.ts index 2fd26ee56f2e..053f585b48f7 100644 --- a/packages/angular_devkit/build_angular/src/builders/browser-esbuild/watcher.ts +++ b/packages/angular_devkit/build_angular/src/builders/browser-esbuild/watcher.ts @@ -25,8 +25,8 @@ export class ChangedFiles { } export interface BuildWatcher extends AsyncIterableIterator { - add(paths: string | string[]): void; - remove(paths: string | string[]): void; + add(paths: string | readonly string[]): void; + remove(paths: string | readonly string[]): void; close(): Promise; }