From a0cf5a5baba9938a2177364bc96ffee346371273 Mon Sep 17 00:00:00 2001 From: Alan Agius <17563226+alan-agius4@users.noreply.github.com> Date: Tue, 20 Oct 2026 09:31:19 +0000 Subject: [PATCH 1/3] fix(@angular/build): use component file path in inline stylesheet diagnostic locations Rewrite internal angular:styles/component:;; virtual URIs in inline component stylesheet error, warning, and note locations to the component's file path, and ensure the component file path is added to watchFiles on error. --- .../esbuild/angular/component-stylesheets.ts | 36 ++++++-- .../angular/component-stylesheets_spec.ts | 89 +++++++++++++++++++ 2 files changed, 119 insertions(+), 6 deletions(-) create mode 100644 packages/angular/build/src/tools/esbuild/angular/component-stylesheets_spec.ts diff --git a/packages/angular/build/src/tools/esbuild/angular/component-stylesheets.ts b/packages/angular/build/src/tools/esbuild/angular/component-stylesheets.ts index b5d2e453b57e..eff47ddf9bd4 100644 --- a/packages/angular/build/src/tools/esbuild/angular/component-stylesheets.ts +++ b/packages/angular/build/src/tools/esbuild/angular/component-stylesheets.ts @@ -176,13 +176,37 @@ export class ComponentStylesheetBundler { ); }); + const result = await bundlerContext.bundle(); + const watchFile = path.isAbsolute(filename) + ? filename + : path.join(this.options.workspaceRoot, filename); + if (result.errors) { + for (const error of result.errors) { + if (error.location?.file.startsWith('angular:styles/component:')) { + error.location.file = filename; + bundlerContext.watchFiles.add(watchFile); + } + for (const note of error.notes) { + if (note.location?.file.startsWith('angular:styles/component:')) { + note.location.file = filename; + bundlerContext.watchFiles.add(watchFile); + } + } + } + } + for (const warning of result.warnings) { + if (warning.location?.file.startsWith('angular:styles/component:')) { + warning.location.file = filename; + } + for (const note of warning.notes) { + if (note.location?.file.startsWith('angular:styles/component:')) { + note.location.file = filename; + } + } + } + // Extract the result of the bundling from the output files - return this.extractResult( - await bundlerContext.bundle(), - bundlerContext.watchFiles, - !!externalId, - false, - ); + return this.extractResult(result, bundlerContext.watchFiles, !!externalId, false); } /** diff --git a/packages/angular/build/src/tools/esbuild/angular/component-stylesheets_spec.ts b/packages/angular/build/src/tools/esbuild/angular/component-stylesheets_spec.ts new file mode 100644 index 000000000000..bdd4cf9a83f7 --- /dev/null +++ b/packages/angular/build/src/tools/esbuild/angular/component-stylesheets_spec.ts @@ -0,0 +1,89 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import assert from 'node:assert'; +import { mkdtemp, rm } from 'node:fs/promises'; +import { join } from 'node:path'; +import { initializeHash } from '../../../utils/hash'; +import { ComponentStylesheetBundler } from './component-stylesheets'; + +describe('ComponentStylesheetBundler', () => { + let tempRoot: string; + + beforeAll(async () => { + await initializeHash(); + const baseTmpDir = process.env['TEST_TMPDIR']; + assert(baseTmpDir, 'TEST_TMPDIR is not set'); + tempRoot = await mkdtemp(join(baseTmpDir, 'angular-cli-component-stylesheets-')); + }); + + afterAll(async () => { + await rm(tempRoot, { force: true, recursive: true }); + }); + + it('uses the component filename instead of angular:styles/component virtual URI in bundleInline error locations', async () => { + const bundler = new ComponentStylesheetBundler( + { + workspaceRoot: tempRoot, + optimization: false, + inlineFonts: false, + sourcemap: false, + outputNames: { bundles: '[name]', media: 'media/[name]' }, + target: ['es2022'], + cacheOptions: { enabled: false, path: '', basePath: tempRoot }, + }, + 'css', + false, + ); + + const componentFile = join(tempRoot, 'src', 'lib', 'lib.component.ts'); + + try { + const result = await bundler.bundleInline( + '.icon { background: url("./missing.png"); }', + componentFile, + ); + + assert(result.errors, 'Expected bundleInline to report a resolution error'); + expect(result.errors.length).toBeGreaterThan(0); + expect(result.errors[0].location?.file).toBe(componentFile); + expect(result.errors[0].location?.file).not.toContain('angular:styles/component:'); + expect(result.referencedFiles).toContain(componentFile); + } finally { + await bundler.dispose(); + } + }); + + it('uses the component filename instead of angular:styles/component virtual URI in bundleInline warning locations', async () => { + const bundler = new ComponentStylesheetBundler( + { + workspaceRoot: tempRoot, + optimization: false, + inlineFonts: false, + sourcemap: false, + outputNames: { bundles: '[name]', media: 'media/[name]' }, + target: ['es2022'], + cacheOptions: { enabled: false, path: '', basePath: tempRoot }, + }, + 'css', + false, + ); + + const componentFile = join(tempRoot, 'src', 'lib', 'lib.component.ts'); + + try { + const result = await bundler.bundleInline('.box { color: red', componentFile); + + expect(result.warnings.length).toBeGreaterThan(0); + expect(result.warnings[0].location?.file).toBe(componentFile); + expect(result.warnings[0].location?.file).not.toContain('angular:styles/component:'); + } finally { + await bundler.dispose(); + } + }); +}); From 4b037263c44ec2271e8f5661b64615cb547ffed1 Mon Sep 17 00:00:00 2001 From: Alan Agius <17563226+alan-agius4@users.noreply.github.com> Date: Tue, 20 Oct 2026 09:45:12 +0000 Subject: [PATCH 2/3] fixup! fix(@angular/build): use component file path in inline stylesheet diagnostic locations --- .../esbuild/angular/component-stylesheets.ts | 26 +++++++------------ 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/packages/angular/build/src/tools/esbuild/angular/component-stylesheets.ts b/packages/angular/build/src/tools/esbuild/angular/component-stylesheets.ts index eff47ddf9bd4..f32af490b74c 100644 --- a/packages/angular/build/src/tools/esbuild/angular/component-stylesheets.ts +++ b/packages/angular/build/src/tools/esbuild/angular/component-stylesheets.ts @@ -180,27 +180,19 @@ export class ComponentStylesheetBundler { const watchFile = path.isAbsolute(filename) ? filename : path.join(this.options.workspaceRoot, filename); - if (result.errors) { - for (const error of result.errors) { - if (error.location?.file.startsWith('angular:styles/component:')) { - error.location.file = filename; + for (const message of [...(result.errors ?? []), ...(result.warnings ?? [])]) { + if (message.location?.file?.startsWith('angular:styles/component:')) { + message.location.file = filename; + if (result.errors) { bundlerContext.watchFiles.add(watchFile); } - for (const note of error.notes) { - if (note.location?.file.startsWith('angular:styles/component:')) { - note.location.file = filename; - bundlerContext.watchFiles.add(watchFile); - } - } } - } - for (const warning of result.warnings) { - if (warning.location?.file.startsWith('angular:styles/component:')) { - warning.location.file = filename; - } - for (const note of warning.notes) { - if (note.location?.file.startsWith('angular:styles/component:')) { + for (const note of message.notes ?? []) { + if (note.location?.file?.startsWith('angular:styles/component:')) { note.location.file = filename; + if (result.errors) { + bundlerContext.watchFiles.add(watchFile); + } } } } From 38dfad4c44092d289c97a1a4240dd5d05f70774a Mon Sep 17 00:00:00 2001 From: Alan Agius <17563226+alan-agius4@users.noreply.github.com> Date: Tue, 20 Oct 2026 13:03:43 +0000 Subject: [PATCH 3/3] fixup! fix(@angular/build): use component file path in inline stylesheet diagnostic locations --- .../esbuild/angular/component-stylesheets.ts | 23 ++++++++----------- .../angular/component-stylesheets_spec.ts | 4 +--- 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/packages/angular/build/src/tools/esbuild/angular/component-stylesheets.ts b/packages/angular/build/src/tools/esbuild/angular/component-stylesheets.ts index f32af490b74c..f8b048ce5459 100644 --- a/packages/angular/build/src/tools/esbuild/angular/component-stylesheets.ts +++ b/packages/angular/build/src/tools/esbuild/angular/component-stylesheets.ts @@ -177,21 +177,16 @@ export class ComponentStylesheetBundler { }); const result = await bundlerContext.bundle(); - const watchFile = path.isAbsolute(filename) - ? filename - : path.join(this.options.workspaceRoot, filename); - for (const message of [...(result.errors ?? []), ...(result.warnings ?? [])]) { - if (message.location?.file?.startsWith('angular:styles/component:')) { - message.location.file = filename; - if (result.errors) { - bundlerContext.watchFiles.add(watchFile); - } + if (result.errors || result.warnings.length > 0) { + if (this.incremental && result.errors) { + bundlerContext.watchFiles.add( + path.isAbsolute(filename) ? filename : path.join(this.options.workspaceRoot, filename), + ); } - for (const note of message.notes ?? []) { - if (note.location?.file?.startsWith('angular:styles/component:')) { - note.location.file = filename; - if (result.errors) { - bundlerContext.watchFiles.add(watchFile); + for (const message of [...(result.errors ?? []), ...result.warnings]) { + for (const item of [message, ...(message.notes ?? [])]) { + if (item.location?.file?.startsWith('angular:styles/component:')) { + item.location.file = filename; } } } diff --git a/packages/angular/build/src/tools/esbuild/angular/component-stylesheets_spec.ts b/packages/angular/build/src/tools/esbuild/angular/component-stylesheets_spec.ts index bdd4cf9a83f7..0363f2f43ba9 100644 --- a/packages/angular/build/src/tools/esbuild/angular/component-stylesheets_spec.ts +++ b/packages/angular/build/src/tools/esbuild/angular/component-stylesheets_spec.ts @@ -38,7 +38,7 @@ describe('ComponentStylesheetBundler', () => { cacheOptions: { enabled: false, path: '', basePath: tempRoot }, }, 'css', - false, + true, ); const componentFile = join(tempRoot, 'src', 'lib', 'lib.component.ts'); @@ -52,7 +52,6 @@ describe('ComponentStylesheetBundler', () => { assert(result.errors, 'Expected bundleInline to report a resolution error'); expect(result.errors.length).toBeGreaterThan(0); expect(result.errors[0].location?.file).toBe(componentFile); - expect(result.errors[0].location?.file).not.toContain('angular:styles/component:'); expect(result.referencedFiles).toContain(componentFile); } finally { await bundler.dispose(); @@ -81,7 +80,6 @@ describe('ComponentStylesheetBundler', () => { expect(result.warnings.length).toBeGreaterThan(0); expect(result.warnings[0].location?.file).toBe(componentFile); - expect(result.warnings[0].location?.file).not.toContain('angular:styles/component:'); } finally { await bundler.dispose(); }