Skip to content

Commit b31787a

Browse files
committed
refactor(@angular/build): use ETag headers for development external component styles
When using the development server, Angular component stylesheet requests that use hot replacement (default for v19) will now use the `ETag` header to remove the need to reprocess and resend encapsulated component styles if the styles have not changed since last usage. The ETag value used is a combination of the style content and the encapsulation component identifier. (cherry picked from commit b73b98d)
1 parent 0793c78 commit b31787a

File tree

3 files changed

+14
-2
lines changed

3 files changed

+14
-2
lines changed

packages/angular/build/src/builders/dev-server/vite-server.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ import type { DevServerBuilderOutput } from './output';
4242
interface OutputFileRecord {
4343
contents: Uint8Array;
4444
size: number;
45-
hash?: string;
45+
hash: string;
4646
updated: boolean;
4747
servable: boolean;
4848
type: BuildOutputFileType;
@@ -540,6 +540,7 @@ function analyzeResultFiles(
540540
contents: file.contents,
541541
servable,
542542
size: file.contents.byteLength,
543+
hash: file.hash,
543544
type: file.type,
544545
updated: false,
545546
});

packages/angular/build/src/tools/vite/middlewares/assets-middleware.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,13 @@ export function createAngularAssetsMiddleware(
7878
// Inject component ID for view encapsulation if requested
7979
const componentId = new URL(req.url, 'http://localhost').searchParams.get('ngcomp');
8080
if (componentId !== null) {
81+
const etag = `W/"${outputFile.contents.byteLength}-${outputFile.hash}-${componentId}"`;
82+
if (req.headers['if-none-match'] === etag) {
83+
res.statusCode = 304;
84+
res.end();
85+
86+
return;
87+
}
8188
// Record the component style usage for HMR updates
8289
const usedIds = usedComponentStyles.get(pathname);
8390
if (usedIds === undefined) {
@@ -98,6 +105,7 @@ export function createAngularAssetsMiddleware(
98105

99106
res.setHeader('Content-Type', 'text/css');
100107
res.setHeader('Cache-Control', 'no-cache');
108+
res.setHeader('ETag', etag);
101109
res.end(encapsulatedData);
102110
})
103111
.catch((e) => next(e));

packages/angular/build/src/tools/vite/utils.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@
99
import { lookup as lookupMimeType } from 'mrmime';
1010
import { extname } from 'node:path';
1111

12-
export type AngularMemoryOutputFiles = Map<string, { contents: Uint8Array; servable: boolean }>;
12+
export type AngularMemoryOutputFiles = Map<
13+
string,
14+
{ contents: Uint8Array; hash: string; servable: boolean }
15+
>;
1316

1417
export function pathnameWithoutBasePath(url: string, basePath: string): string {
1518
const parsedUrl = new URL(url, 'http://localhost');

0 commit comments

Comments
 (0)