@@ -11,8 +11,11 @@ import {
1111 InjectionToken ,
1212 Provider ,
1313 Type ,
14+ inject ,
1415 makeEnvironmentProviders ,
16+ provideEnvironmentInitializer ,
1517} from '@angular/core' ;
18+ import { provideServerRendering as provideServerRenderingPlatformServer } from '@angular/platform-server' ;
1619import { type DefaultExport , ROUTES , type Route } from '@angular/router' ;
1720
1821/**
@@ -22,25 +25,26 @@ import { type DefaultExport, ROUTES, type Route } from '@angular/router';
2225const APP_SHELL_ROUTE = 'ng-app-shell' ;
2326
2427/**
25- * Identifies a particular kind of `ServerRoutesFeatureKind `.
26- * @see {@link ServerRoutesFeature }
28+ * Identifies a particular kind of `ServerRenderingFeatureKind `.
29+ * @see {@link ServerRenderingFeature }
2730 */
28- enum ServerRoutesFeatureKind {
31+ enum ServerRenderingFeatureKind {
2932 AppShell ,
33+ ServerRoutes ,
3034}
3135
3236/**
3337 * Helper type to represent a server routes feature.
34- * @see {@link ServerRoutesFeatureKind }
38+ * @see {@link ServerRenderingFeatureKind }
3539 */
36- interface ServerRoutesFeature < FeatureKind extends ServerRoutesFeatureKind > {
40+ interface ServerRenderingFeature < FeatureKind extends ServerRenderingFeatureKind > {
3741 ɵkind : FeatureKind ;
38- ɵproviders : Provider [ ] ;
42+ ɵproviders : ( Provider | EnvironmentProviders ) [ ] ;
3943}
4044
4145/**
4246 * Different rendering modes for server routes.
43- * @see {@link provideServerRouting }
47+ * @see {@link withRoutes }
4448 * @see {@link ServerRoute }
4549 */
4650export enum RenderMode {
@@ -171,7 +175,7 @@ export interface ServerRouteServer extends ServerRouteCommon {
171175
172176/**
173177 * Server route configuration.
174- * @see {@link provideServerRouting }
178+ * @see {@link withRoutes }
175179 */
176180export type ServerRoute =
177181 | ServerRouteClient
@@ -200,62 +204,103 @@ export interface ServerRoutesConfig {
200204export const SERVER_ROUTES_CONFIG = new InjectionToken < ServerRoutesConfig > ( 'SERVER_ROUTES_CONFIG' ) ;
201205
202206/**
203- * Sets up the necessary providers for configuring server routes.
204- * This function accepts an array of server routes and optional configuration
205- * options, returning an `EnvironmentProviders` object that encapsulates
206- * the server routes and configuration settings.
207+ * Configures server-side routing for the application.
207208 *
208- * @param routes - An array of server routes to be provided.
209- * @param features - (Optional) server routes features.
210- * @returns An `EnvironmentProviders` instance with the server routes configuration .
209+ * This function registers an array of `ServerRoute` definitions, enabling server-side rendering
210+ * for specific URL paths. These routes are used to pre-render content on the server, improving
211+ * initial load performance and SEO .
211212 *
213+ * @param routes - An array of `ServerRoute` objects, each defining a server-rendered route.
214+ * @returns A `ServerRenderingFeature` object configuring server-side routes.
215+ *
216+ * @example
217+ * ```ts
218+ * import { provideServerRendering, withRoutes, ServerRoute, RenderMode } from '@angular/ssr';
219+ *
220+ * const serverRoutes: ServerRoute[] = [
221+ * {
222+ * route: '', // This renders the "/" route on the client (CSR)
223+ * renderMode: RenderMode.Client,
224+ * },
225+ * {
226+ * route: 'about', // This page is static, so we prerender it (SSG)
227+ * renderMode: RenderMode.Prerender,
228+ * },
229+ * {
230+ * route: 'profile', // This page requires user-specific data, so we use SSR
231+ * renderMode: RenderMode.Server,
232+ * },
233+ * {
234+ * route: '**', // All other routes will be rendered on the server (SSR)
235+ * renderMode: RenderMode.Server,
236+ * },
237+ * ];
238+ *
239+ * provideServerRendering(withRoutes(serverRoutes));
240+ * ```
241+ *
242+ * @see {@link provideServerRendering }
212243 * @see {@link ServerRoute }
213- * @see {@link withAppShell }
214244 */
215- export function provideServerRouting (
245+ export function withRoutes (
216246 routes : ServerRoute [ ] ,
217- ...features : ServerRoutesFeature < ServerRoutesFeatureKind > [ ]
218- ) : EnvironmentProviders {
247+ ) : ServerRenderingFeature < ServerRenderingFeatureKind . ServerRoutes > {
219248 const config : ServerRoutesConfig = { routes } ;
220- const hasAppShell = features . some ( ( f ) => f . ɵkind === ServerRoutesFeatureKind . AppShell ) ;
221- if ( hasAppShell ) {
222- config . appShellRoute = APP_SHELL_ROUTE ;
223- }
224-
225- const providers : Provider [ ] = [
226- {
227- provide : SERVER_ROUTES_CONFIG ,
228- useValue : config ,
229- } ,
230- ] ;
231-
232- for ( const feature of features ) {
233- providers . push ( ...feature . ɵproviders ) ;
234- }
235249
236- return makeEnvironmentProviders ( providers ) ;
250+ return {
251+ ɵkind : ServerRenderingFeatureKind . ServerRoutes ,
252+ ɵproviders : [
253+ {
254+ provide : SERVER_ROUTES_CONFIG ,
255+ useValue : config ,
256+ } ,
257+ ] ,
258+ } ;
237259}
238260
239261/**
240- * Configures the app shell route with the provided component.
262+ * Configures the shell of the application.
263+ *
264+ * The app shell is a minimal, static HTML page that is served immediately, while the
265+ * full Angular application loads in the background. This improves perceived performance
266+ * by providing instant feedback to the user.
267+ *
268+ * This function configures the app shell route, which serves the provided component for
269+ * requests that do not match any defined server routes.
241270 *
242- * The app shell serves as the main entry point for the application and is commonly used
243- * to enable server-side rendering (SSR) of the application shell. It handles requests
244- * that do not match any specific server route, providing a fallback mechanism and improving
245- * perceived performance during navigation.
271+ * @param component - The Angular component to render for the app shell. Can be a direct
272+ * component type or a dynamic import function.
273+ * @returns A `ServerRenderingFeature` object configuring the app shell.
246274 *
247- * This configuration is particularly useful in applications leveraging Progressive Web App (PWA)
248- * patterns, such as service workers, to deliver a seamless user experience.
275+ * @example
276+ * ```ts
277+ * import { provideServerRendering, withAppShell, withRoutes } from '@angular/ssr';
278+ * import { AppShellComponent } from './app-shell.component';
249279 *
250- * @param component The Angular component to render for the app shell route.
251- * @returns A server routes feature configuration for the app shell.
280+ * provideServerRendering(
281+ * withRoutes(serverRoutes),
282+ * withAppShell(AppShellComponent)
283+ * );
284+ * ```
252285 *
253- * @see {@link provideServerRouting }
286+ * @example
287+ * ```ts
288+ * import { provideServerRendering, withAppShell, withRoutes } from '@angular/ssr';
289+ *
290+ * provideServerRendering(
291+ * withRoutes(serverRoutes),
292+ * withAppShell(() =>
293+ * import('./app-shell.component').then((m) => m.AppShellComponent)
294+ * )
295+ * );
296+ * ```
297+ *
298+ * @see {@link provideServerRendering }
254299 * @see {@link https://angular.dev/ecosystem/service-workers/app-shell | App shell pattern on Angular.dev }
255300 */
256301export function withAppShell (
257302 component : Type < unknown > | ( ( ) => Promise < Type < unknown > | DefaultExport < Type < unknown > > > ) ,
258- ) : ServerRoutesFeature < ServerRoutesFeatureKind . AppShell > {
303+ ) : ServerRenderingFeature < ServerRenderingFeatureKind . AppShell > {
259304 const routeConfig : Route = {
260305 path : APP_SHELL_ROUTE ,
261306 } ;
@@ -267,13 +312,73 @@ export function withAppShell(
267312 }
268313
269314 return {
270- ɵkind : ServerRoutesFeatureKind . AppShell ,
315+ ɵkind : ServerRenderingFeatureKind . AppShell ,
271316 ɵproviders : [
272317 {
273318 provide : ROUTES ,
274319 useValue : routeConfig ,
275320 multi : true ,
276321 } ,
322+ provideEnvironmentInitializer ( ( ) => {
323+ const config = inject ( SERVER_ROUTES_CONFIG ) ;
324+ config . appShellRoute = APP_SHELL_ROUTE ;
325+ } ) ,
277326 ] ,
278327 } ;
279328}
329+
330+ /**
331+ * Configures server-side rendering for an Angular application.
332+ *
333+ * This function sets up the necessary providers for server-side rendering, including
334+ * support for server routes and app shell. It combines features configured using
335+ * `withRoutes` and `withAppShell` to provide a comprehensive server-side rendering setup.
336+ *
337+ * @param features - Optional features to configure additional server rendering behaviors.
338+ * @returns An `EnvironmentProviders` instance with the server-side rendering configuration.
339+ *
340+ * @example
341+ * Basic example of how you can enable server-side rendering in your application
342+ * when using the `bootstrapApplication` function:
343+ *
344+ * ```ts
345+ * import { bootstrapApplication } from '@angular/platform-browser';
346+ * import { provideServerRendering, withRoutes, withAppShell } from '@angular/ssr';
347+ * import { AppComponent } from './app/app.component';
348+ * import { SERVER_ROUTES } from './app/app.server.routes';
349+ * import { AppShellComponent } from './app/app-shell.component';
350+ *
351+ * bootstrapApplication(AppComponent, {
352+ * providers: [
353+ * provideServerRendering(
354+ * withRoutes(SERVER_ROUTES),
355+ * withAppShell(AppShellComponent)
356+ * )
357+ * ]
358+ * });
359+ * ```
360+ * @see {@link withRoutes } configures server-side routing
361+ * @see {@link withAppShell } configures the application shell
362+ */
363+ export function provideServerRendering (
364+ ...features : ServerRenderingFeature < ServerRenderingFeatureKind > [ ]
365+ ) : EnvironmentProviders {
366+ let hasAppShell = false ;
367+ let hasServerRoutes = false ;
368+ const providers : ( Provider | EnvironmentProviders ) [ ] = [ provideServerRenderingPlatformServer ( ) ] ;
369+
370+ for ( const { ɵkind, ɵproviders } of features ) {
371+ hasAppShell ||= ɵkind === ServerRenderingFeatureKind . AppShell ;
372+ hasServerRoutes ||= ɵkind === ServerRenderingFeatureKind . ServerRoutes ;
373+ providers . push ( ...ɵproviders ) ;
374+ }
375+
376+ if ( ! hasServerRoutes && hasAppShell ) {
377+ throw new Error (
378+ `Configuration error: found 'withAppShell()' without 'withRoutes()' in the same call to 'provideServerRendering()'.` +
379+ `The 'withAppShell()' function requires 'withRoutes()' to be used.` ,
380+ ) ;
381+ }
382+
383+ return makeEnvironmentProviders ( providers ) ;
384+ }
0 commit comments