@@ -16,9 +16,13 @@ import { BuildOutputAsset } from '../../tools/esbuild/bundler-execution-result';
16
16
import { generateIndexHtml } from '../../tools/esbuild/index-html-generator' ;
17
17
import { createOutputFile } from '../../tools/esbuild/utils' ;
18
18
import { maxWorkers } from '../../utils/environment-options' ;
19
+ import {
20
+ SERVER_APP_MANIFEST_FILENAME ,
21
+ generateAngularServerAppManifest ,
22
+ } from '../../utils/server-rendering/manifest' ;
19
23
import { prerenderPages } from '../../utils/server-rendering/prerender' ;
20
24
import { augmentAppWithServiceWorkerEsbuild } from '../../utils/service-worker' ;
21
- import { NormalizedApplicationBuildOptions } from './options' ;
25
+ import { INDEX_HTML_SERVER , NormalizedApplicationBuildOptions } from './options' ;
22
26
23
27
/**
24
28
* Run additional builds steps including SSG, AppShell, Index HTML file and Service worker generation.
@@ -48,25 +52,22 @@ export async function executePostBundleSteps(
48
52
const prerenderedRoutes : string [ ] = [ ] ;
49
53
50
54
const {
55
+ baseHref = '/' ,
51
56
serviceWorker,
52
57
indexHtmlOptions,
53
58
optimizationOptions,
54
59
sourcemapOptions,
60
+ ssrOptions,
55
61
prerenderOptions,
56
62
appShellOptions,
57
63
workspaceRoot,
58
64
verbose,
59
65
} = options ;
60
66
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.
70
71
const additionalHtmlOutputFiles = new Map < string , BuildOutputFile > ( ) ;
71
72
72
73
// Generate index HTML file
@@ -88,21 +89,34 @@ export async function executePostBundleSteps(
88
89
) ;
89
90
90
91
if ( ssrContent ) {
91
- const serverIndexHtmlFilename = 'index.server.html' ;
92
92
additionalHtmlOutputFiles . set (
93
- serverIndexHtmlFilename ,
94
- createOutputFile ( serverIndexHtmlFilename , ssrContent , BuildOutputFileType . Server ) ,
93
+ INDEX_HTML_SERVER ,
94
+ createOutputFile ( INDEX_HTML_SERVER , ssrContent , BuildOutputFileType . Server ) ,
95
95
) ;
96
-
97
- ssrIndexContent = ssrContent ;
98
96
}
99
97
}
100
98
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
+
101
115
// Pre-render (SSG) and App-shell
102
116
// If localization is enabled, prerendering is handled in the inlining process.
103
- if ( prerenderOptions || appShellOptions ) {
117
+ if ( ( prerenderOptions || appShellOptions ) && ! allErrors . length ) {
104
118
assert (
105
- ssrIndexContent ,
119
+ indexHtmlOptions ,
106
120
'The "index" option is required when using the "ssg" or "appShell" options.' ,
107
121
) ;
108
122
@@ -111,15 +125,15 @@ export async function executePostBundleSteps(
111
125
warnings,
112
126
errors,
113
127
prerenderedRoutes : generatedRoutes ,
128
+ serializableRouteTreeNode,
114
129
} = await prerenderPages (
115
130
workspaceRoot ,
131
+ baseHref ,
116
132
appShellOptions ,
117
133
prerenderOptions ,
118
- outputFiles ,
134
+ [ ... outputFiles , ... additionalOutputFiles ] ,
119
135
assetFiles ,
120
- ssrIndexContent ,
121
136
sourcemapOptions . scripts ,
122
- optimizationOptions . styles . inlineCritical ,
123
137
maxWorkers ,
124
138
verbose ,
125
139
) ;
@@ -128,10 +142,31 @@ export async function executePostBundleSteps(
128
142
allWarnings . push ( ...warnings ) ;
129
143
prerenderedRoutes . push ( ...Array . from ( generatedRoutes ) ) ;
130
144
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 ;
132
152
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
+ ) ,
135
170
) ;
136
171
}
137
172
}
@@ -145,7 +180,7 @@ export async function executePostBundleSteps(
145
180
const serviceWorkerResult = await augmentAppWithServiceWorkerEsbuild (
146
181
workspaceRoot ,
147
182
serviceWorker ,
148
- options . baseHref || '/' ,
183
+ baseHref ,
149
184
options . indexHtmlOptions ?. output ,
150
185
// Ensure additional files recently added are used
151
186
[ ...outputFiles , ...additionalOutputFiles ] ,
0 commit comments