6
6
* found in the LICENSE file at https://angular.dev/license
7
7
*/
8
8
9
- import { StaticProvider , ɵConsole , ɵresetCompiledComponents } from '@angular/core' ;
10
- import { ɵSERVER_CONTEXT as SERVER_CONTEXT } from '@angular/platform-server' ;
9
+ import { StaticProvider , ɵresetCompiledComponents } from '@angular/core' ;
11
10
import { ServerAssets } from './assets' ;
12
- import { Console } from './console' ;
13
11
import { Hooks } from './hooks' ;
14
12
import { getAngularAppManifest } from './manifest' ;
13
+ import { RenderMode } from './routes/route-config' ;
15
14
import { ServerRouter } from './routes/router' ;
16
15
import { REQUEST , REQUEST_CONTEXT , RESPONSE_INIT } from './tokens' ;
17
16
import { InlineCriticalCssProcessor } from './utils/inline-critical-css' ;
18
17
import { renderAngular } from './utils/ng' ;
19
18
20
19
/**
21
- * Enum representing the different contexts in which server rendering can occur.
20
+ * A mapping of `RenderMode` enum values to corresponding string representations.
21
+ *
22
+ * This record is used to map each `RenderMode` to a specific string value that represents
23
+ * the server context. The string values are used internally to differentiate
24
+ * between various rendering strategies when processing routes.
25
+ *
26
+ * - `RenderMode.SSG` maps to `'ssg'` (Static Site Generation).
27
+ * - `RenderMode.SSR` maps to `'ssr'` (Server-Side Rendering).
28
+ * - `RenderMode.AppShell` maps to `'app-shell'` (pre-rendered application shell).
29
+ * - `RenderMode.CSR` maps to an empty string `''` (Client-Side Rendering, no server context needed).
22
30
*/
23
- export enum ServerRenderContext {
24
- SSR = 'ssr' ,
25
- SSG = 'ssg' ,
26
- AppShell = 'app-shell' ,
27
- }
31
+ const SERVER_CONTEXT_VALUE : Record < RenderMode , string > = {
32
+ [ RenderMode . SSG ] : 'ssg' ,
33
+ [ RenderMode . SSR ] : 'ssr' ,
34
+ [ RenderMode . AppShell ] : 'app-shell' ,
35
+ [ RenderMode . CSR ] : '' ,
36
+ } ;
28
37
29
38
/**
30
39
* Represents a locale-specific Angular server application managed by the server application engine.
@@ -65,18 +74,31 @@ export class AngularServerApp {
65
74
*
66
75
* @param request - The incoming HTTP request to be rendered.
67
76
* @param requestContext - Optional additional context for rendering, such as request metadata.
68
- * @param serverContext - The rendering context.
69
77
*
70
78
* @returns A promise that resolves to the HTTP response object resulting from the rendering, or null if no match is found.
71
79
*/
72
- render (
73
- request : Request ,
74
- requestContext ?: unknown ,
75
- serverContext : ServerRenderContext = ServerRenderContext . SSR ,
76
- ) : Promise < Response | null > {
80
+ render ( request : Request , requestContext ?: unknown ) : Promise < Response | null > {
77
81
return Promise . race ( [
78
82
this . createAbortPromise ( request ) ,
79
- this . handleRendering ( request , requestContext , serverContext ) ,
83
+ this . handleRendering ( request , /** isSsrMode */ true , requestContext ) ,
84
+ ] ) ;
85
+ }
86
+
87
+ /**
88
+ * Renders a response for the given URL using the server application.
89
+ *
90
+ * This method processes the request based on the provided URL and returns a response by performing server-side rendering.
91
+ * The rendering process can be aborted, with the first completed promise (either the abort or the render) determining the result.
92
+ *
93
+ * @param url - The URL of the page url. This object contains the full URL to be processed and rendered by the server.
94
+ * @returns A promise that resolves to the HTTP response object generated from the rendering, or `null` if no matching route is found.
95
+ */
96
+ renderStatic ( url : URL ) : Promise < Response | null > {
97
+ const request = new Request ( url ) ;
98
+
99
+ return Promise . race ( [
100
+ this . createAbortPromise ( request ) ,
101
+ this . handleRendering ( request , /** isSsrMode */ false ) ,
80
102
] ) ;
81
103
}
82
104
@@ -107,15 +129,15 @@ export class AngularServerApp {
107
129
* This method matches the request URL to a route and performs rendering if a matching route is found.
108
130
*
109
131
* @param request - The incoming HTTP request to be processed.
132
+ * @param isSsrMode - A boolean indicating whether the rendering is performed in server-side rendering (SSR) mode.
110
133
* @param requestContext - Optional additional context for rendering, such as request metadata.
111
- * @param serverContext - The rendering context. Defaults to server-side rendering (SSR).
112
134
*
113
135
* @returns A promise that resolves to the rendered response, or null if no matching route is found.
114
136
*/
115
137
private async handleRendering (
116
138
request : Request ,
139
+ isSsrMode : boolean ,
117
140
requestContext ?: unknown ,
118
- serverContext : ServerRenderContext = ServerRenderContext . SSR ,
119
141
) : Promise < Response | null > {
120
142
const url = new URL ( request . url ) ;
121
143
this . router ??= await ServerRouter . from ( this . manifest , url ) ;
@@ -126,32 +148,32 @@ export class AngularServerApp {
126
148
return null ;
127
149
}
128
150
129
- const { redirectTo } = matchedRoute ;
151
+ const { redirectTo, status } = matchedRoute ;
130
152
if ( redirectTo !== undefined ) {
153
+ // Note: The status code is validated during route extraction.
131
154
// 302 Found is used by default for redirections
132
155
// See: https://developer.mozilla.org/en-US/docs/Web/API/Response/redirect_static#status
133
- return Response . redirect ( new URL ( redirectTo , url ) , 302 ) ;
156
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
157
+ return Response . redirect ( new URL ( redirectTo , url ) , ( status as any ) ?? 302 ) ;
134
158
}
135
159
136
- const platformProviders : StaticProvider [ ] = [
137
- {
138
- provide : SERVER_CONTEXT ,
139
- useValue : serverContext ,
140
- } ,
141
- {
142
- // An Angular Console Provider that does not print a set of predefined logs.
143
- provide : ɵConsole ,
144
- // Using `useClass` would necessitate decorating `Console` with `@Injectable`,
145
- // which would require switching from `ts_library` to `ng_module`. This change
146
- // would also necessitate various patches of `@angular/bazel` to support ESM.
147
- useFactory : ( ) => new Console ( ) ,
148
- } ,
149
- ] ;
150
-
151
- const isSsrMode = serverContext === ServerRenderContext . SSR ;
152
- const responseInit : ResponseInit = { } ;
160
+ const { renderMode = isSsrMode ? RenderMode . SSR : RenderMode . SSG , headers } = matchedRoute ;
161
+
162
+ const platformProviders : StaticProvider [ ] = [ ] ;
163
+ let responseInit : ResponseInit | undefined ;
153
164
154
165
if ( isSsrMode ) {
166
+ // Initialize the response with status and headers if available.
167
+ responseInit = {
168
+ status,
169
+ headers : headers ? new Headers ( headers ) : undefined ,
170
+ } ;
171
+
172
+ if ( renderMode === RenderMode . CSR ) {
173
+ // Serve the client-side rendered version if the route is configured for CSR.
174
+ return new Response ( await this . assets . getServerAsset ( 'index.csr.html' ) , responseInit ) ;
175
+ }
176
+
155
177
platformProviders . push (
156
178
{
157
179
provide : REQUEST ,
@@ -183,7 +205,13 @@ export class AngularServerApp {
183
205
html = await hooks . run ( 'html:transform:pre' , { html } ) ;
184
206
}
185
207
186
- html = await renderAngular ( html , manifest . bootstrap ( ) , new URL ( request . url ) , platformProviders ) ;
208
+ html = await renderAngular (
209
+ html ,
210
+ manifest . bootstrap ( ) ,
211
+ new URL ( request . url ) ,
212
+ platformProviders ,
213
+ SERVER_CONTEXT_VALUE [ renderMode ] ,
214
+ ) ;
187
215
188
216
if ( manifest . inlineCriticalCss ) {
189
217
// Optionally inline critical CSS.
0 commit comments