@@ -16,9 +16,13 @@ import { BuildOutputAsset } from '../../tools/esbuild/bundler-execution-result';
1616import { generateIndexHtml } from '../../tools/esbuild/index-html-generator' ;
1717import { createOutputFile } from '../../tools/esbuild/utils' ;
1818import { maxWorkers } from '../../utils/environment-options' ;
19+ import {
20+ SERVER_APP_MANIFEST_FILENAME ,
21+ generateAngularServerAppManifest ,
22+ } from '../../utils/server-rendering/manifest' ;
1923import { prerenderPages } from '../../utils/server-rendering/prerender' ;
2024import { augmentAppWithServiceWorkerEsbuild } from '../../utils/service-worker' ;
21- import { NormalizedApplicationBuildOptions } from './options' ;
25+ import { INDEX_HTML_SERVER , NormalizedApplicationBuildOptions } from './options' ;
2226
2327/**
2428 * Run additional builds steps including SSG, AppShell, Index HTML file and Service worker generation.
@@ -48,25 +52,22 @@ export async function executePostBundleSteps(
4852 const prerenderedRoutes : string [ ] = [ ] ;
4953
5054 const {
55+ baseHref = '/' ,
5156 serviceWorker,
5257 indexHtmlOptions,
5358 optimizationOptions,
5459 sourcemapOptions,
60+ ssrOptions,
5561 prerenderOptions,
5662 appShellOptions,
5763 workspaceRoot,
5864 verbose,
5965 } = options ;
6066
61- /**
62- * Index HTML content without CSS inlining to be used for server rendering (AppShell, SSG and SSR).
63- *
64- * NOTE: we don't perform critical CSS inlining as this will be done during server rendering.
65- */
66- let ssrIndexContent : string | undefined ;
67-
68- // When using prerender/app-shell the index HTML file can be regenerated.
69- // Thus, we use a Map so that we do not generate 2 files with the same filename.
67+ // Index HTML content without CSS inlining to be used for server rendering (AppShell, SSG and SSR).
68+ // NOTE: Critical CSS inlining is deliberately omitted here, as it will be handled during server rendering.
69+ // Additionally, when using prerendering or AppShell, the index HTML file may be regenerated.
70+ // To prevent generating duplicate files with the same filename, a `Map` is used to store and manage the files.
7071 const additionalHtmlOutputFiles = new Map < string , BuildOutputFile > ( ) ;
7172
7273 // Generate index HTML file
@@ -88,21 +89,34 @@ export async function executePostBundleSteps(
8889 ) ;
8990
9091 if ( ssrContent ) {
91- const serverIndexHtmlFilename = 'index.server.html' ;
9292 additionalHtmlOutputFiles . set (
93- serverIndexHtmlFilename ,
94- createOutputFile ( serverIndexHtmlFilename , ssrContent , BuildOutputFileType . Server ) ,
93+ INDEX_HTML_SERVER ,
94+ createOutputFile ( INDEX_HTML_SERVER , ssrContent , BuildOutputFileType . Server ) ,
9595 ) ;
96-
97- ssrIndexContent = ssrContent ;
9896 }
9997 }
10098
99+ // Create server manifest
100+ if ( prerenderOptions || appShellOptions || ssrOptions ) {
101+ additionalOutputFiles . push (
102+ createOutputFile (
103+ SERVER_APP_MANIFEST_FILENAME ,
104+ generateAngularServerAppManifest (
105+ additionalHtmlOutputFiles ,
106+ outputFiles ,
107+ optimizationOptions . styles . inlineCritical ?? false ,
108+ undefined ,
109+ ) ,
110+ BuildOutputFileType . Server ,
111+ ) ,
112+ ) ;
113+ }
114+
101115 // Pre-render (SSG) and App-shell
102116 // If localization is enabled, prerendering is handled in the inlining process.
103- if ( prerenderOptions || appShellOptions ) {
117+ if ( ( prerenderOptions || appShellOptions ) && ! allErrors . length ) {
104118 assert (
105- ssrIndexContent ,
119+ indexHtmlOptions ,
106120 'The "index" option is required when using the "ssg" or "appShell" options.' ,
107121 ) ;
108122
@@ -111,15 +125,15 @@ export async function executePostBundleSteps(
111125 warnings,
112126 errors,
113127 prerenderedRoutes : generatedRoutes ,
128+ serializableRouteTreeNode,
114129 } = await prerenderPages (
115130 workspaceRoot ,
131+ baseHref ,
116132 appShellOptions ,
117133 prerenderOptions ,
118- outputFiles ,
134+ [ ... outputFiles , ... additionalOutputFiles ] ,
119135 assetFiles ,
120- ssrIndexContent ,
121136 sourcemapOptions . scripts ,
122- optimizationOptions . styles . inlineCritical ,
123137 maxWorkers ,
124138 verbose ,
125139 ) ;
@@ -128,10 +142,31 @@ export async function executePostBundleSteps(
128142 allWarnings . push ( ...warnings ) ;
129143 prerenderedRoutes . push ( ...Array . from ( generatedRoutes ) ) ;
130144
131- for ( const [ path , content ] of Object . entries ( output ) ) {
145+ const indexHasBeenPrerendered = generatedRoutes . has ( indexHtmlOptions . output ) ;
146+
147+ for ( const [ path , { content, appShellRoute } ] of Object . entries ( output ) ) {
148+ // Update the index contents with the app shell under these conditions:
149+ // - Replace 'index.html' with the app shell only if it hasn't been prerendered yet.
150+ // - Always replace 'index.csr.html' with the app shell.
151+ const filePath = appShellRoute && ! indexHasBeenPrerendered ? indexHtmlOptions . output : path ;
132152 additionalHtmlOutputFiles . set (
133- path ,
134- createOutputFile ( path , content , BuildOutputFileType . Browser ) ,
153+ filePath ,
154+ createOutputFile ( filePath , content , BuildOutputFileType . Browser ) ,
155+ ) ;
156+ }
157+
158+ if ( ssrOptions ) {
159+ // Regenerate the manifest to append route tree. This is only needed if SSR is enabled.
160+ const manifest = additionalOutputFiles . find ( ( f ) => f . path === SERVER_APP_MANIFEST_FILENAME ) ;
161+ assert ( manifest , `${ SERVER_APP_MANIFEST_FILENAME } was not found in output files.` ) ;
162+
163+ manifest . contents = new TextEncoder ( ) . encode (
164+ generateAngularServerAppManifest (
165+ additionalHtmlOutputFiles ,
166+ outputFiles ,
167+ optimizationOptions . styles . inlineCritical ?? false ,
168+ serializableRouteTreeNode ,
169+ ) ,
135170 ) ;
136171 }
137172 }
@@ -145,7 +180,7 @@ export async function executePostBundleSteps(
145180 const serviceWorkerResult = await augmentAppWithServiceWorkerEsbuild (
146181 workspaceRoot ,
147182 serviceWorker ,
148- options . baseHref || '/' ,
183+ baseHref ,
149184 options . indexHtmlOptions ?. output ,
150185 // Ensure additional files recently added are used
151186 [ ...outputFiles , ...additionalOutputFiles ] ,
0 commit comments