Skip to content

Commit 5cacd34

Browse files
committed
fix(@angular-devkit/build-angular): watch all TypeScript referenced files in esbuild builder
When using the esbuild-based browser application builder in watch mode, all files referenced by the TypeScript program are now watched in additional to files within the project root. This allows for more extensive monorepo setups to take advantage of watch mode as TypeScript files may exist in other library projects within the repository.
1 parent b674bd2 commit 5cacd34

File tree

6 files changed

+52
-7
lines changed

6 files changed

+52
-7
lines changed

packages/angular_devkit/build_angular/src/builders/browser-esbuild/angular/angular-compilation.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,11 @@ export abstract class AngularCompilation {
5353
tsconfig: string,
5454
hostOptions: AngularHostOptions,
5555
compilerOptionsTransformer?: (compilerOptions: ng.CompilerOptions) => ng.CompilerOptions,
56-
): Promise<{ affectedFiles: ReadonlySet<ts.SourceFile>; compilerOptions: ng.CompilerOptions }>;
56+
): Promise<{
57+
affectedFiles: ReadonlySet<ts.SourceFile>;
58+
compilerOptions: ng.CompilerOptions;
59+
referencedFiles: readonly string[];
60+
}>;
5761

5862
abstract collectDiagnostics(): Iterable<ts.Diagnostic>;
5963

packages/angular_devkit/build_angular/src/builders/browser-esbuild/angular/aot-compilation.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,11 @@ export class AotCompilation extends AngularCompilation {
4343
tsconfig: string,
4444
hostOptions: AngularHostOptions,
4545
compilerOptionsTransformer?: (compilerOptions: ng.CompilerOptions) => ng.CompilerOptions,
46-
): Promise<{ affectedFiles: ReadonlySet<ts.SourceFile>; compilerOptions: ng.CompilerOptions }> {
46+
): Promise<{
47+
affectedFiles: ReadonlySet<ts.SourceFile>;
48+
compilerOptions: ng.CompilerOptions;
49+
referencedFiles: readonly string[];
50+
}> {
4751
// Dynamically load the Angular compiler CLI package
4852
const { NgtscProgram, OptimizeFor } = await AngularCompilation.loadCompilerCli();
4953

@@ -96,7 +100,12 @@ export class AotCompilation extends AngularCompilation {
96100
this.#state?.diagnosticCache,
97101
);
98102

99-
return { affectedFiles, compilerOptions };
103+
const referencedFiles = typeScriptProgram
104+
.getSourceFiles()
105+
.filter((sourceFile) => !angularCompiler.ignoreForEmit.has(sourceFile))
106+
.map((sourceFile) => sourceFile.fileName);
107+
108+
return { affectedFiles, compilerOptions, referencedFiles };
100109
}
101110

102111
*collectDiagnostics(): Iterable<ts.Diagnostic> {

packages/angular_devkit/build_angular/src/builders/browser-esbuild/angular/compiler-plugin.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ export class SourceFileCache extends Map<string, ts.SourceFile> {
4545
readonly typeScriptFileCache = new Map<string, string | Uint8Array>();
4646
readonly loadResultCache = new MemoryLoadResultCache();
4747

48+
referencedFiles?: readonly string[];
49+
4850
constructor(readonly persistentCachePath?: string) {
4951
super();
5052
}
@@ -185,6 +187,7 @@ export function createCompilerPlugin(
185187
// In watch mode, previous build state will be reused.
186188
const {
187189
compilerOptions: { allowJs },
190+
referencedFiles,
188191
} = await compilation.initialize(tsconfigPath, hostOptions, (compilerOptions) => {
189192
if (
190193
compilerOptions.target === undefined ||
@@ -254,6 +257,11 @@ export function createCompilerPlugin(
254257
}
255258
});
256259

260+
// Store referenced files for updated file watching if enabled
261+
if (pluginOptions.sourceFileCache) {
262+
pluginOptions.sourceFileCache.referencedFiles = referencedFiles;
263+
}
264+
257265
// Reset the setup warnings so that they are only shown during the first build.
258266
setupWarnings = undefined;
259267

packages/angular_devkit/build_angular/src/builders/browser-esbuild/angular/jit-compilation.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,11 @@ export class JitCompilation extends AngularCompilation {
3030
tsconfig: string,
3131
hostOptions: AngularHostOptions,
3232
compilerOptionsTransformer?: (compilerOptions: ng.CompilerOptions) => ng.CompilerOptions,
33-
): Promise<{ affectedFiles: ReadonlySet<ts.SourceFile>; compilerOptions: ng.CompilerOptions }> {
33+
): Promise<{
34+
affectedFiles: ReadonlySet<ts.SourceFile>;
35+
compilerOptions: ng.CompilerOptions;
36+
referencedFiles: readonly string[];
37+
}> {
3438
// Dynamically load the Angular compiler CLI package
3539
const { constructorParametersDownlevelTransform } = await AngularCompilation.loadCompilerCli();
3640

@@ -68,7 +72,11 @@ export class JitCompilation extends AngularCompilation {
6872
createJitResourceTransformer(() => typeScriptProgram.getProgram().getTypeChecker()),
6973
);
7074

71-
return { affectedFiles, compilerOptions };
75+
const referencedFiles = typeScriptProgram
76+
.getSourceFiles()
77+
.map((sourceFile) => sourceFile.fileName);
78+
79+
return { affectedFiles, compilerOptions, referencedFiles };
7280
}
7381

7482
*collectDiagnostics(): Iterable<ts.Diagnostic> {

packages/angular_devkit/build_angular/src/builders/browser-esbuild/index.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ class ExecutionResult {
7373
};
7474
}
7575

76+
get watchFiles() {
77+
return this.codeBundleCache?.referencedFiles ?? [];
78+
}
79+
7680
createRebuildState(fileChanges: ChangedFiles): RebuildState {
7781
this.codeBundleCache?.invalidate([...fileChanges.modified, ...fileChanges.removed]);
7882

@@ -676,6 +680,10 @@ export async function* buildEsbuildBrowserInternal(
676680
];
677681
watcher.add(packageWatchFiles.map((file) => path.join(normalizedOptions.workspaceRoot, file)));
678682

683+
// Watch locations provided by the initial build result
684+
let previousWatchFiles = new Set(result.watchFiles);
685+
watcher.add(result.watchFiles);
686+
679687
// Wait for changes and rebuild as needed
680688
try {
681689
for await (const changes of watcher) {
@@ -687,6 +695,14 @@ export async function* buildEsbuildBrowserInternal(
687695
execute(normalizedOptions, context, result.createRebuildState(changes)),
688696
);
689697

698+
// Update watched locations provided by the new build result.
699+
// Add any new locations
700+
watcher.add(result.watchFiles.filter((watchFile) => !previousWatchFiles.has(watchFile)));
701+
const newWatchFiles = new Set(result.watchFiles);
702+
// Remove any old locations
703+
watcher.remove([...previousWatchFiles].filter((watchFile) => !newWatchFiles.has(watchFile)));
704+
previousWatchFiles = newWatchFiles;
705+
690706
if (shouldWriteResult) {
691707
// Write output files
692708
await writeResultFiles(result.outputFiles, result.assetFiles, normalizedOptions.outputPath);

packages/angular_devkit/build_angular/src/builders/browser-esbuild/watcher.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ export class ChangedFiles {
2525
}
2626

2727
export interface BuildWatcher extends AsyncIterableIterator<ChangedFiles> {
28-
add(paths: string | string[]): void;
29-
remove(paths: string | string[]): void;
28+
add(paths: string | readonly string[]): void;
29+
remove(paths: string | readonly string[]): void;
3030
close(): Promise<void>;
3131
}
3232

0 commit comments

Comments
 (0)