@@ -129,6 +129,11 @@ export class AngularServerApp {
129129 */
130130 private boostrap : AngularBootstrap | undefined ;
131131
132+ /**
133+ * Decorder used to convert a string to a Uint8Array.
134+ */
135+ private readonly textDecoder = new TextEncoder ( ) ;
136+
132137 /**
133138 * Cache for storing critical CSS for pages.
134139 * Stores a maximum of MAX_INLINE_CSS_CACHE_ENTRIES entries.
@@ -318,30 +323,46 @@ export class AngularServerApp {
318323 SERVER_CONTEXT_VALUE [ renderMode ] ,
319324 ) ;
320325
321- if ( inlineCriticalCss ) {
322- // Optionally inline critical CSS.
323- this . inlineCriticalCssProcessor ??= new InlineCriticalCssProcessor ( ( path : string ) => {
324- const fileName = path . split ( '/' ) . pop ( ) ?? path ;
325-
326- return this . assets . getServerAsset ( fileName ) . text ( ) ;
327- } ) ;
326+ if ( ! inlineCriticalCss ) {
327+ return new Response ( html , responseInit ) ;
328+ }
328329
329- if ( renderMode === RenderMode . Server ) {
330- // Only cache if we are running in SSR Mode.
331- const cacheKey = await sha256 ( html ) ;
332- let htmlWithCriticalCss = this . criticalCssLRUCache . get ( cacheKey ) ;
333- if ( htmlWithCriticalCss === undefined ) {
334- htmlWithCriticalCss = await this . inlineCriticalCssProcessor . process ( html ) ;
335- this . criticalCssLRUCache . put ( cacheKey , htmlWithCriticalCss ) ;
330+ this . inlineCriticalCssProcessor ??= new InlineCriticalCssProcessor ( ( path : string ) => {
331+ const fileName = path . split ( '/' ) . pop ( ) ?? path ;
332+
333+ return this . assets . getServerAsset ( fileName ) . text ( ) ;
334+ } ) ;
335+
336+ const { inlineCriticalCssProcessor, criticalCssLRUCache, textDecoder } = this ;
337+
338+ // Use a stream to send the response before inlining critical CSS, improving performance via header flushing.
339+ const stream = new ReadableStream ( {
340+ async start ( controller ) {
341+ let htmlWithCriticalCss ;
342+
343+ try {
344+ if ( renderMode === RenderMode . Server ) {
345+ const cacheKey = await sha256 ( html ) ;
346+ htmlWithCriticalCss = criticalCssLRUCache . get ( cacheKey ) ;
347+ if ( ! htmlWithCriticalCss ) {
348+ htmlWithCriticalCss = await inlineCriticalCssProcessor . process ( html ) ;
349+ criticalCssLRUCache . put ( cacheKey , htmlWithCriticalCss ) ;
350+ }
351+ } else {
352+ htmlWithCriticalCss = await inlineCriticalCssProcessor . process ( html ) ;
353+ }
354+ } catch ( error ) {
355+ // eslint-disable-next-line no-console
356+ console . error ( `An error occurred while inlining critical CSS for: ${ url } .` , error ) ;
336357 }
337358
338- html = htmlWithCriticalCss ;
339- } else {
340- html = await this . inlineCriticalCssProcessor . process ( html ) ;
341- }
342- }
359+ controller . enqueue ( textDecoder . encode ( htmlWithCriticalCss ?? html ) ) ;
360+
361+ controller . close ( ) ;
362+ } ,
363+ } ) ;
343364
344- return new Response ( html , responseInit ) ;
365+ return new Response ( stream , responseInit ) ;
345366 }
346367
347368 /**
0 commit comments