From de9eae8703418a6e8fd59a63d8194a0463f37a8f Mon Sep 17 00:00:00 2001 From: Alan Agius <17563226+alan-agius4@users.noreply.github.com> Date: Wed, 21 Oct 2026 16:45:00 +0000 Subject: [PATCH 1/3] fix(@angular/build): improve library compilation diagnostics and stylesheet reference tracking - Forward warnings from `compilationInstance.initialize()` into formatted warnings output. - Run syntactic and option diagnostics (`DiagnosticModes.All & ~DiagnosticModes.Semantic`) when `useTypeChecking` is `false`. - Run `diagnoseFiles` even when `stylesheetErrors.length > 0` so `AotCompilation` drains `affectedFiles` into `diagnosticCache`, and guard `diagnosePromise` against unhandled rejections. - Pass `'css'` for inline `
Styled
', + 'projects/lib/src/lib/lib.component.ts': ` + import { Component } from "@angular/core"; + + @Component({ + selector: "lib-html-style", + templateUrl: "./lib.component.html", + }) + export class LibComponent {} + `, + }); + + harness.useTarget('build', { + ...BASE_OPTIONS, + inlineStyleLanguage: InlineStyleLanguage.Sass, + }); + + const { result } = await harness.executeOnce(); + expect(result?.success).toBeTrue(); + + const fesm = harness.readFile('dist/lib/fesm2022/lib.mjs'); + expect(fesm).toMatch(/color:\s*#ff5722/); + }); }); }); diff --git a/packages/angular/build/src/builders/library/tests/behavior/watch_spec.ts b/packages/angular/build/src/builders/library/tests/behavior/watch_spec.ts index b648c690951a..e117f8efe9f8 100644 --- a/packages/angular/build/src/builders/library/tests/behavior/watch_spec.ts +++ b/packages/angular/build/src/builders/library/tests/behavior/watch_spec.ts @@ -1058,5 +1058,105 @@ describeLibraryBuilder(executeLibraryBuilder, LIBRARY_BUILDER_INFO, (harness) => }, ]); }); + + it('should preserve template diagnostics across watch rebuilds after a stylesheet error', async () => { + await harness.modifyFile('projects/lib/tsconfig.lib.json', (content) => { + const tsconfig = JSON.parse(content); + tsconfig.angularCompilerOptions = { + ...tsconfig.angularCompilerOptions, + strictTemplates: true, + }; + + return JSON.stringify(tsconfig, null, 2); + }); + + await harness.writeFiles({ + 'projects/lib/src/lib/other.component.ts': ` + import { Component } from '@angular/core'; + + @Component({ + selector: 'lib-other', + template: '{{ validProp }}', + }) + export class OtherComponent { + validProp = 'ok'; + } + `, + 'projects/lib/src/lib/lib.component.scss': '.valid { color: blue; }', + 'projects/lib/src/lib/lib.component.ts': ` + import { Component } from '@angular/core'; + + @Component({ + selector: 'lib-primary', + template: 'Primary', + styleUrl: './lib.component.scss', + }) + export class LibComponent {} + `, + 'projects/lib/src/public-api.ts': ` + export * from './lib/lib.component'; + export * from './lib/other.component'; + `, + }); + + harness.useTarget('build', { + ...BASE_OPTIONS, + watch: true, + }); + + await harness.executeWithCases([ + async ({ result }) => { + expect(result?.success).toBeTrue(); + + // Introduce both a stylesheet error in LibComponent and a template error in OtherComponent + await harness.writeFiles({ + 'projects/lib/src/lib/lib.component.scss': '.broken { color: $undefined_var; }', + 'projects/lib/src/lib/other.component.ts': ` + import { Component } from '@angular/core'; + + @Component({ + selector: 'lib-other', + template: '{{ unknownProp }}', + }) + export class OtherComponent {} + `, + }); + }, + async ({ result, logs }) => { + expect(result?.success).toBeFalse(); + expect(logs).toContain( + jasmine.objectContaining({ + level: 'error', + message: jasmine.stringContaining('Undefined variable'), + }), + ); + expect(logs).toContain( + jasmine.objectContaining({ + level: 'error', + message: jasmine.stringContaining( + "Property 'unknownProp' does not exist on type 'OtherComponent'", + ), + }), + ); + + // Fix only the stylesheet error in LibComponent while OtherComponent still has the template error + await harness.writeFile( + 'projects/lib/src/lib/lib.component.scss', + '.valid { color: green; }', + ); + }, + ({ result, logs }) => { + expect(result?.success).toBeFalse(); + expect(logs).toContain( + jasmine.objectContaining({ + level: 'error', + message: jasmine.stringContaining( + "Property 'unknownProp' does not exist on type 'OtherComponent'", + ), + }), + ); + }, + ]); + }); }); }); From eb6d6df79a6d5cf9beed28ad5e3000e4ba98b4e3 Mon Sep 17 00:00:00 2001 From: Alan Agius <17563226+alan-agius4@users.noreply.github.com> Date: Thu, 22 Oct 2026 18:14:40 +0000 Subject: [PATCH 2/3] fixup! fix(@angular/build): improve library compilation diagnostics and stylesheet reference tracking --- .../builders/library/pipeline/compilation.ts | 64 +++++++++++-------- .../library/pipeline/compilation_spec.ts | 1 - 2 files changed, 36 insertions(+), 29 deletions(-) diff --git a/packages/angular/build/src/builders/library/pipeline/compilation.ts b/packages/angular/build/src/builders/library/pipeline/compilation.ts index 9aa4691eb13a..2a1adddaee50 100644 --- a/packages/angular/build/src/builders/library/pipeline/compilation.ts +++ b/packages/angular/build/src/builders/library/pipeline/compilation.ts @@ -7,7 +7,7 @@ */ import { type PartialMessage, formatMessages } from 'esbuild'; -import { existsSync } from 'node:fs'; +import { access, constants } from 'node:fs/promises'; import { type AngularCompilation, DiagnosticModes, @@ -172,7 +172,7 @@ export async function compileLibrary( const esmFiles = cached?.esmFiles ?? new Map(); const dtsFiles = cached?.dtsFiles ?? new Map(); const posixReferencedFiles = new Set(referencedFiles.map(toPosixPath)); - const { changedEsmFiles, changedDtsFiles } = updateEmittedFileCaches( + const { changedEsmFiles, changedDtsFiles } = await updateEmittedFileCaches( emittedFiles, esmFiles, dtsFiles, @@ -216,40 +216,49 @@ export async function compileLibrary( * @param modifiedFiles Optional set of modified POSIX file paths in incremental builds. * @returns Sets of changed ESM and DTS file paths. */ -function updateEmittedFileCaches( +async function updateEmittedFileCaches( emittedFiles: Iterable, esmFiles: Map, dtsFiles: Map, posixReferencedFiles: ReadonlySet, modifiedFiles?: ReadonlySet, -): { changedEsmFiles: Set; changedDtsFiles: Set } { +): Promise<{ changedEsmFiles: Set; changedDtsFiles: Set }> { const changedEsmFiles = new Set(); const changedDtsFiles = new Set(); - if (modifiedFiles) { - for (const modifiedFile of modifiedFiles) { - if (posixReferencedFiles.has(modifiedFile)) { - continue; - } - - const basePathWithoutExt = modifiedFile.replace(/(?:\.d\.[cm]?ts|\.[cm]?[jt]sx?)$/i, ''); - if (basePathWithoutExt === modifiedFile || existsSync(modifiedFile)) { - continue; - } + const removalChecks: Promise[] = []; + for (const modifiedFile of modifiedFiles ?? []) { + if (posixReferencedFiles.has(modifiedFile)) { + continue; + } - for (const ext of EMITTED_EXTENSIONS) { - const outputPath = `${basePathWithoutExt}${ext}`; - const mapPath = `${outputPath}.map`; - if (esmFiles.delete(outputPath)) { - changedEsmFiles.add(outputPath); - } - if (dtsFiles.delete(outputPath)) { - changedDtsFiles.add(outputPath); - } - esmFiles.delete(mapPath); - dtsFiles.delete(mapPath); - } + const basePathWithoutExt = modifiedFile.replace(/(?:\.d\.[cm]?ts|\.[cm]?[jt]sx?)$/i, ''); + if (basePathWithoutExt === modifiedFile) { + continue; } + + removalChecks.push( + access(modifiedFile, constants.F_OK).then( + () => {}, + () => { + for (const ext of EMITTED_EXTENSIONS) { + const outputPath = `${basePathWithoutExt}${ext}`; + const mapPath = `${outputPath}.map`; + if (esmFiles.delete(outputPath)) { + changedEsmFiles.add(outputPath); + } + if (dtsFiles.delete(outputPath)) { + changedDtsFiles.add(outputPath); + } + esmFiles.delete(mapPath); + dtsFiles.delete(mapPath); + } + }, + ), + ); + } + if (removalChecks.length > 0) { + await Promise.all(removalChecks); } for (const { filename, contents } of emittedFiles) { @@ -301,9 +310,8 @@ async function runDiagnosticsAndFormat( } catch (error) { assertIsError(error); errors.push({ - text: 'Angular compilation diagnostics failed.', + text: error.message, location: null, - notes: [{ text: error.stack ?? error.message, location: null }], }); } diff --git a/packages/angular/build/src/builders/library/pipeline/compilation_spec.ts b/packages/angular/build/src/builders/library/pipeline/compilation_spec.ts index 802e64c24713..143a73358e57 100644 --- a/packages/angular/build/src/builders/library/pipeline/compilation_spec.ts +++ b/packages/angular/build/src/builders/library/pipeline/compilation_spec.ts @@ -212,7 +212,6 @@ describe('compileLibrary pipeline', () => { const errorsText = state.executionResult.errors.join('\n'); expect(errorsText).toContain('Sass syntax error'); - expect(errorsText).toContain('Angular compilation diagnostics failed.'); expect(errorsText).toContain('Diagnostic crash'); }); From ed5cc15c36681b5b0184736a5adc094ad93fabfd Mon Sep 17 00:00:00 2001 From: Alan Agius <17563226+alan-agius4@users.noreply.github.com> Date: Thu, 22 Oct 2026 18:21:26 +0000 Subject: [PATCH 3/3] fixup! fix(@angular/build): improve library compilation diagnostics and stylesheet reference tracking --- .../angular/build/src/builders/library/pipeline/compilation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/angular/build/src/builders/library/pipeline/compilation.ts b/packages/angular/build/src/builders/library/pipeline/compilation.ts index 2a1adddaee50..07dd0665c08a 100644 --- a/packages/angular/build/src/builders/library/pipeline/compilation.ts +++ b/packages/angular/build/src/builders/library/pipeline/compilation.ts @@ -214,7 +214,7 @@ export async function compileLibrary( * @param dtsFiles In-memory map of POSIX declaration file paths to contents. * @param posixReferencedFiles Set of POSIX file paths currently referenced by the program. * @param modifiedFiles Optional set of modified POSIX file paths in incremental builds. - * @returns Sets of changed ESM and DTS file paths. + * @returns A promise resolving to the sets of changed ESM and DTS file paths. */ async function updateEmittedFileCaches( emittedFiles: Iterable,