@angular/ssr 19.0.0-next.5 → 19.0.0-next.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/node.mjs +3 -3
- package/fesm2022/node.mjs.map +1 -1
- package/fesm2022/ssr.mjs +278 -61
- package/fesm2022/ssr.mjs.map +1 -1
- package/index.d.ts +182 -46
- package/node/index.d.ts +2 -2
- package/package.json +1 -1
package/fesm2022/ssr.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { APP_BASE_HREF, PlatformLocation } from '@angular/common';
|
|
2
|
-
import { ɵConsole as _Console, createPlatformFactory, platformCore, ApplicationRef, ɵwhenStable as _whenStable, Compiler,
|
|
3
|
-
import { renderModule, renderApplication, INITIAL_CONFIG, ɵINTERNAL_SERVER_PLATFORM_PROVIDERS as _INTERNAL_SERVER_PLATFORM_PROVIDERS
|
|
2
|
+
import { ɵConsole as _Console, InjectionToken, makeEnvironmentProviders, runInInjectionContext, createPlatformFactory, platformCore, ApplicationRef, ɵwhenStable as _whenStable, Compiler, ɵresetCompiledComponents as _resetCompiledComponents } from '@angular/core';
|
|
3
|
+
import { ɵSERVER_CONTEXT as _SERVER_CONTEXT, renderModule, renderApplication, INITIAL_CONFIG, ɵINTERNAL_SERVER_PLATFORM_PROVIDERS as _INTERNAL_SERVER_PLATFORM_PROVIDERS } from '@angular/platform-server';
|
|
4
4
|
import { ɵloadChildren as _loadChildren, Router } from '@angular/router';
|
|
5
5
|
import Critters from '../third_party/critters/index.js';
|
|
6
6
|
|
|
@@ -217,21 +217,38 @@ function stripIndexHtmlFromURL(url) {
|
|
|
217
217
|
* correctly handle route-based rendering.
|
|
218
218
|
* @param platformProviders - An array of platform providers to be used during the
|
|
219
219
|
* rendering process.
|
|
220
|
+
* @param serverContext - A string representing the server context, used to provide additional
|
|
221
|
+
* context or metadata during server-side rendering.
|
|
220
222
|
* @returns A promise that resolves to a string containing the rendered HTML.
|
|
221
223
|
*/
|
|
222
|
-
function renderAngular(html, bootstrap, url, platformProviders) {
|
|
224
|
+
function renderAngular(html, bootstrap, url, platformProviders, serverContext) {
|
|
225
|
+
const providers = [
|
|
226
|
+
{
|
|
227
|
+
provide: _SERVER_CONTEXT,
|
|
228
|
+
useValue: serverContext,
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
// An Angular Console Provider that does not print a set of predefined logs.
|
|
232
|
+
provide: _Console,
|
|
233
|
+
// Using `useClass` would necessitate decorating `Console` with `@Injectable`,
|
|
234
|
+
// which would require switching from `ts_library` to `ng_module`. This change
|
|
235
|
+
// would also necessitate various patches of `@angular/bazel` to support ESM.
|
|
236
|
+
useFactory: () => new Console(),
|
|
237
|
+
},
|
|
238
|
+
...platformProviders,
|
|
239
|
+
];
|
|
223
240
|
// A request to `http://www.example.com/page/index.html` will render the Angular route corresponding to `http://www.example.com/page`.
|
|
224
241
|
const urlToRender = stripIndexHtmlFromURL(url).toString();
|
|
225
242
|
return isNgModule(bootstrap)
|
|
226
243
|
? renderModule(bootstrap, {
|
|
227
244
|
url: urlToRender,
|
|
228
245
|
document: html,
|
|
229
|
-
extraProviders:
|
|
246
|
+
extraProviders: providers,
|
|
230
247
|
})
|
|
231
248
|
: renderApplication(bootstrap, {
|
|
232
249
|
url: urlToRender,
|
|
233
250
|
document: html,
|
|
234
|
-
platformProviders,
|
|
251
|
+
platformProviders: providers,
|
|
235
252
|
});
|
|
236
253
|
}
|
|
237
254
|
/**
|
|
@@ -246,10 +263,72 @@ function isNgModule(value) {
|
|
|
246
263
|
return 'ɵmod' in value;
|
|
247
264
|
}
|
|
248
265
|
|
|
266
|
+
/**
|
|
267
|
+
* Different rendering modes for server routes.
|
|
268
|
+
* @developerPreview
|
|
269
|
+
*/
|
|
270
|
+
var RenderMode;
|
|
271
|
+
(function (RenderMode) {
|
|
272
|
+
/** AppShell rendering mode, typically used for pre-rendered shells of the application. */
|
|
273
|
+
RenderMode[RenderMode["AppShell"] = 0] = "AppShell";
|
|
274
|
+
/** Server-Side Rendering (SSR) mode, where content is rendered on the server for each request. */
|
|
275
|
+
RenderMode[RenderMode["Server"] = 1] = "Server";
|
|
276
|
+
/** Client-Side Rendering (CSR) mode, where content is rendered on the client side in the browser. */
|
|
277
|
+
RenderMode[RenderMode["Client"] = 2] = "Client";
|
|
278
|
+
/** Static Site Generation (SSG) mode, where content is pre-rendered at build time and served as static files. */
|
|
279
|
+
RenderMode[RenderMode["Prerender"] = 3] = "Prerender";
|
|
280
|
+
})(RenderMode || (RenderMode = {}));
|
|
281
|
+
/**
|
|
282
|
+
* Defines the fallback strategies for Static Site Generation (SSG) routes when a pre-rendered path is not available.
|
|
283
|
+
* This is particularly relevant for routes with parameterized URLs where some paths might not be pre-rendered at build time.
|
|
284
|
+
*
|
|
285
|
+
* @developerPreview
|
|
286
|
+
*/
|
|
287
|
+
var PrerenderFallback;
|
|
288
|
+
(function (PrerenderFallback) {
|
|
289
|
+
/**
|
|
290
|
+
* Fallback to Server-Side Rendering (SSR) if the pre-rendered path is not available.
|
|
291
|
+
* This strategy dynamically generates the page on the server at request time.
|
|
292
|
+
*/
|
|
293
|
+
PrerenderFallback[PrerenderFallback["Server"] = 0] = "Server";
|
|
294
|
+
/**
|
|
295
|
+
* Fallback to Client-Side Rendering (CSR) if the pre-rendered path is not available.
|
|
296
|
+
* This strategy allows the page to be rendered on the client side.
|
|
297
|
+
*/
|
|
298
|
+
PrerenderFallback[PrerenderFallback["Client"] = 1] = "Client";
|
|
299
|
+
/**
|
|
300
|
+
* No fallback; if the path is not pre-rendered, the server will not handle the request.
|
|
301
|
+
* This means the application will not provide any response for paths that are not pre-rendered.
|
|
302
|
+
*/
|
|
303
|
+
PrerenderFallback[PrerenderFallback["None"] = 2] = "None";
|
|
304
|
+
})(PrerenderFallback || (PrerenderFallback = {}));
|
|
305
|
+
/**
|
|
306
|
+
* Token for providing the server routes configuration.
|
|
307
|
+
* @internal
|
|
308
|
+
*/
|
|
309
|
+
const SERVER_ROUTES_CONFIG = new InjectionToken('SERVER_ROUTES_CONFIG');
|
|
310
|
+
/**
|
|
311
|
+
* Configures the necessary providers for server routes configuration.
|
|
312
|
+
*
|
|
313
|
+
* @param routes - An array of server routes to be provided.
|
|
314
|
+
* @returns An `EnvironmentProviders` object that contains the server routes configuration.
|
|
315
|
+
* @developerPreview
|
|
316
|
+
*/
|
|
317
|
+
function provideServerRoutesConfig(routes) {
|
|
318
|
+
return makeEnvironmentProviders([
|
|
319
|
+
{
|
|
320
|
+
provide: SERVER_ROUTES_CONFIG,
|
|
321
|
+
useValue: routes,
|
|
322
|
+
},
|
|
323
|
+
]);
|
|
324
|
+
}
|
|
325
|
+
|
|
249
326
|
/**
|
|
250
327
|
* A route tree implementation that supports efficient route matching, including support for wildcard routes.
|
|
251
328
|
* This structure is useful for organizing and retrieving routes in a hierarchical manner,
|
|
252
329
|
* enabling complex routing scenarios with nested paths.
|
|
330
|
+
*
|
|
331
|
+
* @typeParam AdditionalMetadata - Type of additional metadata that can be associated with route nodes.
|
|
253
332
|
*/
|
|
254
333
|
class RouteTree {
|
|
255
334
|
constructor() {
|
|
@@ -422,36 +501,62 @@ class RouteTree {
|
|
|
422
501
|
}
|
|
423
502
|
|
|
424
503
|
/**
|
|
425
|
-
*
|
|
504
|
+
* Regular expression to match segments preceded by a colon in a string.
|
|
505
|
+
*/
|
|
506
|
+
const URL_PARAMETER_REGEXP = /(?<!\\):([^/]+)/g;
|
|
507
|
+
/**
|
|
508
|
+
* An set of HTTP status codes that are considered valid for redirect responses.
|
|
509
|
+
*/
|
|
510
|
+
const VALID_REDIRECT_RESPONSE_CODES = new Set([301, 302, 303, 307, 308]);
|
|
511
|
+
/**
|
|
512
|
+
* Traverses an array of route configurations to generate route tree node metadata.
|
|
426
513
|
*
|
|
427
|
-
*
|
|
428
|
-
*
|
|
514
|
+
* This function processes each route and its children, handling redirects, SSG (Static Site Generation) settings,
|
|
515
|
+
* and lazy-loaded routes. It yields route metadata for each route and its potential variants.
|
|
429
516
|
*
|
|
430
|
-
* @param options -
|
|
431
|
-
* @returns An async iterator
|
|
517
|
+
* @param options - The configuration options for traversing routes.
|
|
518
|
+
* @returns An async iterable iterator of route tree node metadata.
|
|
432
519
|
*/
|
|
433
|
-
async function* traverseRoutesConfig(
|
|
434
|
-
const { routes, compiler, parentInjector, parentRoute } = options;
|
|
520
|
+
async function* traverseRoutesConfig({ routes, compiler, parentInjector, parentRoute, serverConfigRouteTree, invokeGetPrerenderParams, }) {
|
|
435
521
|
for (const route of routes) {
|
|
436
522
|
const { path = '', redirectTo, loadChildren, children } = route;
|
|
437
523
|
const currentRoutePath = joinUrlParts(parentRoute, path);
|
|
438
|
-
|
|
524
|
+
// Get route metadata from the server config route tree, if available
|
|
525
|
+
const metadata = {
|
|
526
|
+
...(serverConfigRouteTree
|
|
527
|
+
? getMatchedRouteMetadata(serverConfigRouteTree, currentRoutePath)
|
|
528
|
+
: undefined),
|
|
439
529
|
route: currentRoutePath,
|
|
440
|
-
redirectTo: typeof redirectTo === 'string'
|
|
441
|
-
? resolveRedirectTo(currentRoutePath, redirectTo)
|
|
442
|
-
: undefined,
|
|
443
530
|
};
|
|
531
|
+
// Handle redirects
|
|
532
|
+
if (typeof redirectTo === 'string') {
|
|
533
|
+
const redirectToResolved = resolveRedirectTo(currentRoutePath, redirectTo);
|
|
534
|
+
if (metadata.status && !VALID_REDIRECT_RESPONSE_CODES.has(metadata.status)) {
|
|
535
|
+
throw new Error(`The '${metadata.status}' status code is not a valid redirect response code. ` +
|
|
536
|
+
`Please use one of the following redirect response codes: ${[...VALID_REDIRECT_RESPONSE_CODES.values()].join(', ')}.`);
|
|
537
|
+
}
|
|
538
|
+
yield { ...metadata, redirectTo: redirectToResolved };
|
|
539
|
+
}
|
|
540
|
+
else if (metadata.renderMode === RenderMode.Prerender) {
|
|
541
|
+
// Handle SSG routes
|
|
542
|
+
yield* handleSSGRoute(metadata, parentInjector, invokeGetPrerenderParams);
|
|
543
|
+
}
|
|
544
|
+
else {
|
|
545
|
+
yield metadata;
|
|
546
|
+
}
|
|
547
|
+
// Recursively process child routes
|
|
444
548
|
if (children?.length) {
|
|
445
|
-
// Recursively process child routes.
|
|
446
549
|
yield* traverseRoutesConfig({
|
|
447
550
|
routes: children,
|
|
448
551
|
compiler,
|
|
449
552
|
parentInjector,
|
|
450
553
|
parentRoute: currentRoutePath,
|
|
554
|
+
serverConfigRouteTree,
|
|
555
|
+
invokeGetPrerenderParams,
|
|
451
556
|
});
|
|
452
557
|
}
|
|
558
|
+
// Load and process lazy-loaded child routes
|
|
453
559
|
if (loadChildren) {
|
|
454
|
-
// Load and process lazy-loaded child routes.
|
|
455
560
|
const loadedChildRoutes = await _loadChildren(route, compiler, parentInjector).toPromise();
|
|
456
561
|
if (loadedChildRoutes) {
|
|
457
562
|
const { routes: childRoutes, injector = parentInjector } = loadedChildRoutes;
|
|
@@ -460,11 +565,77 @@ async function* traverseRoutesConfig(options) {
|
|
|
460
565
|
compiler,
|
|
461
566
|
parentInjector: injector,
|
|
462
567
|
parentRoute: currentRoutePath,
|
|
568
|
+
serverConfigRouteTree,
|
|
569
|
+
invokeGetPrerenderParams,
|
|
463
570
|
});
|
|
464
571
|
}
|
|
465
572
|
}
|
|
466
573
|
}
|
|
467
574
|
}
|
|
575
|
+
/**
|
|
576
|
+
* Retrieves the matched route metadata from the server configuration route tree.
|
|
577
|
+
*
|
|
578
|
+
* @param serverConfigRouteTree - The server configuration route tree.
|
|
579
|
+
* @param currentRoutePath - The current route path being processed.
|
|
580
|
+
* @returns The metadata associated with the matched route.
|
|
581
|
+
*/
|
|
582
|
+
function getMatchedRouteMetadata(serverConfigRouteTree, currentRoutePath) {
|
|
583
|
+
const metadata = serverConfigRouteTree.match(currentRoutePath);
|
|
584
|
+
if (!metadata) {
|
|
585
|
+
throw new Error(`The '${currentRoutePath}' route does not match any route defined in the server routing configuration. ` +
|
|
586
|
+
'Please ensure this route is added to the server routing configuration.');
|
|
587
|
+
}
|
|
588
|
+
return metadata;
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* Handles SSG (Static Site Generation) routes by invoking `getPrerenderParams` and yielding
|
|
592
|
+
* all parameterized paths.
|
|
593
|
+
*
|
|
594
|
+
* @param metadata - The metadata associated with the route tree node.
|
|
595
|
+
* @param parentInjector - The dependency injection container for the parent route.
|
|
596
|
+
* @param invokeGetPrerenderParams - A flag indicating whether to invoke the `getPrerenderParams` function.
|
|
597
|
+
* @returns An async iterable iterator that yields route tree node metadata for each SSG path.
|
|
598
|
+
*/
|
|
599
|
+
async function* handleSSGRoute(metadata, parentInjector, invokeGetPrerenderParams) {
|
|
600
|
+
if (metadata.renderMode !== RenderMode.Prerender) {
|
|
601
|
+
throw new Error(`'handleSSGRoute' was called for a route which rendering mode is not prerender.`);
|
|
602
|
+
}
|
|
603
|
+
const { route: currentRoutePath, fallback, ...meta } = metadata;
|
|
604
|
+
const getPrerenderParams = 'getPrerenderParams' in meta ? meta.getPrerenderParams : undefined;
|
|
605
|
+
if ('getPrerenderParams' in meta) {
|
|
606
|
+
delete meta['getPrerenderParams'];
|
|
607
|
+
}
|
|
608
|
+
if (invokeGetPrerenderParams && URL_PARAMETER_REGEXP.test(currentRoutePath)) {
|
|
609
|
+
if (!getPrerenderParams) {
|
|
610
|
+
throw new Error(`The '${currentRoutePath}' route uses prerendering and includes parameters, but 'getPrerenderParams' is missing. ` +
|
|
611
|
+
`Please define 'getPrerenderParams' function for this route in your server routing configuration ` +
|
|
612
|
+
`or specify a different 'renderMode'.`);
|
|
613
|
+
}
|
|
614
|
+
const parameters = await runInInjectionContext(parentInjector, () => getPrerenderParams());
|
|
615
|
+
for (const params of parameters) {
|
|
616
|
+
const routeWithResolvedParams = currentRoutePath.replace(URL_PARAMETER_REGEXP, (match) => {
|
|
617
|
+
const parameterName = match.slice(1);
|
|
618
|
+
const value = params[parameterName];
|
|
619
|
+
if (typeof value !== 'string') {
|
|
620
|
+
throw new Error(`The 'getPrerenderParams' function defined for the '${currentRoutePath}' route ` +
|
|
621
|
+
`returned a non-string value for parameter '${parameterName}'. ` +
|
|
622
|
+
`Please make sure the 'getPrerenderParams' function returns values for all parameters ` +
|
|
623
|
+
'specified in this route.');
|
|
624
|
+
}
|
|
625
|
+
return value;
|
|
626
|
+
});
|
|
627
|
+
yield { ...meta, route: routeWithResolvedParams };
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
// Handle fallback render modes
|
|
631
|
+
if (fallback !== PrerenderFallback.None || !invokeGetPrerenderParams) {
|
|
632
|
+
yield {
|
|
633
|
+
...meta,
|
|
634
|
+
route: currentRoutePath,
|
|
635
|
+
renderMode: fallback === PrerenderFallback.Client ? RenderMode.Client : RenderMode.Server,
|
|
636
|
+
};
|
|
637
|
+
}
|
|
638
|
+
}
|
|
468
639
|
/**
|
|
469
640
|
* Resolves the `redirectTo` property for a given route.
|
|
470
641
|
*
|
|
@@ -486,24 +657,39 @@ function resolveRedirectTo(routePath, redirectTo) {
|
|
|
486
657
|
segments.pop(); // Remove the last segment to make it relative.
|
|
487
658
|
return joinUrlParts(...segments, redirectTo);
|
|
488
659
|
}
|
|
660
|
+
/**
|
|
661
|
+
* Builds a server configuration route tree from the given server routes configuration.
|
|
662
|
+
*
|
|
663
|
+
* @param serverRoutesConfig - The array of server routes to be used for configuration.
|
|
664
|
+
* @returns A `RouteTree` populated with the server routes and their metadata.
|
|
665
|
+
*/
|
|
666
|
+
function buildServerConfigRouteTree(serverRoutesConfig) {
|
|
667
|
+
const serverConfigRouteTree = new RouteTree();
|
|
668
|
+
for (const { path, ...metadata } of serverRoutesConfig) {
|
|
669
|
+
serverConfigRouteTree.insert(path, metadata);
|
|
670
|
+
}
|
|
671
|
+
return serverConfigRouteTree;
|
|
672
|
+
}
|
|
489
673
|
/**
|
|
490
674
|
* Retrieves routes from the given Angular application.
|
|
491
675
|
*
|
|
492
676
|
* This function initializes an Angular platform, bootstraps the application or module,
|
|
493
677
|
* and retrieves routes from the Angular router configuration. It handles both module-based
|
|
494
|
-
* and function-based bootstrapping. It yields the resulting routes as `
|
|
678
|
+
* and function-based bootstrapping. It yields the resulting routes as `RouteTreeNodeMetadata` objects.
|
|
495
679
|
*
|
|
496
680
|
* @param bootstrap - A function that returns a promise resolving to an `ApplicationRef` or an Angular module to bootstrap.
|
|
497
681
|
* @param document - The initial HTML document used for server-side rendering.
|
|
498
682
|
* This document is necessary to render the application on the server.
|
|
499
683
|
* @param url - The URL for server-side rendering. The URL is used to configure `ServerPlatformLocation`. This configuration is crucial
|
|
500
684
|
* for ensuring that API requests for relative paths succeed, which is essential for accurate route extraction.
|
|
685
|
+
* @param invokeGetPrerenderParams - A boolean flag indicating whether to invoke `getPrerenderParams` for parameterized SSG routes
|
|
686
|
+
* to handle prerendering paths. Defaults to `false`.
|
|
501
687
|
* See:
|
|
502
688
|
* - https://github.com/angular/angular/blob/d608b857c689d17a7ffa33bbb510301014d24a17/packages/platform-server/src/location.ts#L51
|
|
503
689
|
* - https://github.com/angular/angular/blob/6882cc7d9eed26d3caeedca027452367ba25f2b9/packages/platform-server/src/http.ts#L44
|
|
504
690
|
* @returns A promise that resolves to an object of type `AngularRouterConfigResult`.
|
|
505
691
|
*/
|
|
506
|
-
async function getRoutesFromAngularRouterConfig(bootstrap, document, url) {
|
|
692
|
+
async function getRoutesFromAngularRouterConfig(bootstrap, document, url, invokeGetPrerenderParams = false) {
|
|
507
693
|
const { protocol, host } = url;
|
|
508
694
|
// Create and initialize the Angular platform for server-side rendering.
|
|
509
695
|
const platformRef = createPlatformFactory(platformCore, 'server', [
|
|
@@ -533,19 +719,25 @@ async function getRoutesFromAngularRouterConfig(bootstrap, document, url) {
|
|
|
533
719
|
const routesResults = [];
|
|
534
720
|
if (router.config.length) {
|
|
535
721
|
const compiler = injector.get(Compiler);
|
|
722
|
+
const serverRoutesConfig = injector.get(SERVER_ROUTES_CONFIG, null, { optional: true });
|
|
723
|
+
const serverConfigRouteTree = serverRoutesConfig
|
|
724
|
+
? buildServerConfigRouteTree(serverRoutesConfig)
|
|
725
|
+
: undefined;
|
|
536
726
|
// Retrieve all routes from the Angular router configuration.
|
|
537
727
|
const traverseRoutes = traverseRoutesConfig({
|
|
538
728
|
routes: router.config,
|
|
539
729
|
compiler,
|
|
540
730
|
parentInjector: injector,
|
|
541
731
|
parentRoute: '',
|
|
732
|
+
serverConfigRouteTree,
|
|
733
|
+
invokeGetPrerenderParams,
|
|
542
734
|
});
|
|
543
735
|
for await (const result of traverseRoutes) {
|
|
544
736
|
routesResults.push(result);
|
|
545
737
|
}
|
|
546
738
|
}
|
|
547
739
|
else {
|
|
548
|
-
routesResults.push({ route: '' });
|
|
740
|
+
routesResults.push({ route: '', renderMode: RenderMode.Prerender });
|
|
549
741
|
}
|
|
550
742
|
const baseHref = injector.get(APP_BASE_HREF, null, { optional: true }) ??
|
|
551
743
|
injector.get(PlatformLocation).getBaseHrefFromDOM();
|
|
@@ -569,18 +761,21 @@ async function getRoutesFromAngularRouterConfig(bootstrap, document, url) {
|
|
|
569
761
|
* - https://github.com/angular/angular/blob/6882cc7d9eed26d3caeedca027452367ba25f2b9/packages/platform-server/src/http.ts#L44
|
|
570
762
|
* @param manifest - An optional `AngularAppManifest` that contains the application's routing and configuration details.
|
|
571
763
|
* If not provided, the default manifest is retrieved using `getAngularAppManifest()`.
|
|
572
|
-
*
|
|
764
|
+
* @param invokeGetPrerenderParams - A boolean flag indicating whether to invoke `getPrerenderParams` for parameterized SSG routes
|
|
765
|
+
* to handle prerendering paths. Defaults to `false`.
|
|
573
766
|
* @returns A promise that resolves to a populated `RouteTree` containing all extracted routes from the Angular application.
|
|
574
767
|
*/
|
|
575
|
-
async function extractRoutesAndCreateRouteTree(url, manifest = getAngularAppManifest()) {
|
|
768
|
+
async function extractRoutesAndCreateRouteTree(url, manifest = getAngularAppManifest(), invokeGetPrerenderParams = false) {
|
|
576
769
|
const routeTree = new RouteTree();
|
|
577
770
|
const document = await new ServerAssets(manifest).getIndexServerHtml();
|
|
578
771
|
const bootstrap = await manifest.bootstrap();
|
|
579
|
-
const { baseHref, routes } = await getRoutesFromAngularRouterConfig(bootstrap, document, url);
|
|
580
|
-
for (
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
772
|
+
const { baseHref, routes } = await getRoutesFromAngularRouterConfig(bootstrap, document, url, invokeGetPrerenderParams);
|
|
773
|
+
for (const { route, ...metadata } of routes) {
|
|
774
|
+
if (metadata.redirectTo !== undefined) {
|
|
775
|
+
metadata.redirectTo = joinUrlParts(baseHref, metadata.redirectTo);
|
|
776
|
+
}
|
|
777
|
+
const fullRoute = joinUrlParts(baseHref, route);
|
|
778
|
+
routeTree.insert(fullRoute, metadata);
|
|
584
779
|
}
|
|
585
780
|
return routeTree;
|
|
586
781
|
}
|
|
@@ -912,14 +1107,23 @@ class InlineCriticalCssProcessor extends CrittersBase {
|
|
|
912
1107
|
}
|
|
913
1108
|
|
|
914
1109
|
/**
|
|
915
|
-
*
|
|
1110
|
+
* A mapping of `RenderMode` enum values to corresponding string representations.
|
|
1111
|
+
*
|
|
1112
|
+
* This record is used to map each `RenderMode` to a specific string value that represents
|
|
1113
|
+
* the server context. The string values are used internally to differentiate
|
|
1114
|
+
* between various rendering strategies when processing routes.
|
|
1115
|
+
*
|
|
1116
|
+
* - `RenderMode.Prerender` maps to `'ssg'` (Static Site Generation).
|
|
1117
|
+
* - `RenderMode.Server` maps to `'ssr'` (Server-Side Rendering).
|
|
1118
|
+
* - `RenderMode.AppShell` maps to `'app-shell'` (pre-rendered application shell).
|
|
1119
|
+
* - `RenderMode.Client` maps to an empty string `''` (Client-Side Rendering, no server context needed).
|
|
916
1120
|
*/
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
}
|
|
1121
|
+
const SERVER_CONTEXT_VALUE = {
|
|
1122
|
+
[RenderMode.Prerender]: 'ssg',
|
|
1123
|
+
[RenderMode.Server]: 'ssr',
|
|
1124
|
+
[RenderMode.AppShell]: 'app-shell',
|
|
1125
|
+
[RenderMode.Client]: '',
|
|
1126
|
+
};
|
|
923
1127
|
/**
|
|
924
1128
|
* Represents a locale-specific Angular server application managed by the server application engine.
|
|
925
1129
|
*
|
|
@@ -948,14 +1152,29 @@ class AngularServerApp {
|
|
|
948
1152
|
*
|
|
949
1153
|
* @param request - The incoming HTTP request to be rendered.
|
|
950
1154
|
* @param requestContext - Optional additional context for rendering, such as request metadata.
|
|
951
|
-
* @param serverContext - The rendering context.
|
|
952
1155
|
*
|
|
953
1156
|
* @returns A promise that resolves to the HTTP response object resulting from the rendering, or null if no match is found.
|
|
954
1157
|
*/
|
|
955
|
-
render(request, requestContext
|
|
1158
|
+
render(request, requestContext) {
|
|
956
1159
|
return Promise.race([
|
|
957
1160
|
this.createAbortPromise(request),
|
|
958
|
-
this.handleRendering(request,
|
|
1161
|
+
this.handleRendering(request, /** isSsrMode */ true, requestContext),
|
|
1162
|
+
]);
|
|
1163
|
+
}
|
|
1164
|
+
/**
|
|
1165
|
+
* Renders a page based on the provided URL via server-side rendering and returns the corresponding HTTP response.
|
|
1166
|
+
* The rendering process can be interrupted by an abort signal, where the first resolved promise (either from the abort
|
|
1167
|
+
* or the render process) will dictate the outcome.
|
|
1168
|
+
*
|
|
1169
|
+
* @param url - The full URL to be processed and rendered by the server.
|
|
1170
|
+
* @param signal - (Optional) An `AbortSignal` object that allows for the cancellation of the rendering process.
|
|
1171
|
+
* @returns A promise that resolves to the generated HTTP response object, or `null` if no matching route is found.
|
|
1172
|
+
*/
|
|
1173
|
+
renderStatic(url, signal) {
|
|
1174
|
+
const request = new Request(url, { signal });
|
|
1175
|
+
return Promise.race([
|
|
1176
|
+
this.createAbortPromise(request),
|
|
1177
|
+
this.handleRendering(request, /** isSsrMode */ false),
|
|
959
1178
|
]);
|
|
960
1179
|
}
|
|
961
1180
|
/**
|
|
@@ -978,12 +1197,12 @@ class AngularServerApp {
|
|
|
978
1197
|
* This method matches the request URL to a route and performs rendering if a matching route is found.
|
|
979
1198
|
*
|
|
980
1199
|
* @param request - The incoming HTTP request to be processed.
|
|
1200
|
+
* @param isSsrMode - A boolean indicating whether the rendering is performed in server-side rendering (SSR) mode.
|
|
981
1201
|
* @param requestContext - Optional additional context for rendering, such as request metadata.
|
|
982
|
-
* @param serverContext - The rendering context. Defaults to server-side rendering (SSR).
|
|
983
1202
|
*
|
|
984
1203
|
* @returns A promise that resolves to the rendered response, or null if no matching route is found.
|
|
985
1204
|
*/
|
|
986
|
-
async handleRendering(request,
|
|
1205
|
+
async handleRendering(request, isSsrMode, requestContext) {
|
|
987
1206
|
const url = new URL(request.url);
|
|
988
1207
|
this.router ??= await ServerRouter.from(this.manifest, url);
|
|
989
1208
|
const matchedRoute = this.router.match(url);
|
|
@@ -991,29 +1210,27 @@ class AngularServerApp {
|
|
|
991
1210
|
// Not a known Angular route.
|
|
992
1211
|
return null;
|
|
993
1212
|
}
|
|
994
|
-
const { redirectTo } = matchedRoute;
|
|
1213
|
+
const { redirectTo, status } = matchedRoute;
|
|
995
1214
|
if (redirectTo !== undefined) {
|
|
1215
|
+
// Note: The status code is validated during route extraction.
|
|
996
1216
|
// 302 Found is used by default for redirections
|
|
997
1217
|
// See: https://developer.mozilla.org/en-US/docs/Web/API/Response/redirect_static#status
|
|
998
|
-
|
|
1218
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1219
|
+
return Response.redirect(new URL(redirectTo, url), status ?? 302);
|
|
999
1220
|
}
|
|
1000
|
-
const
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
useValue: serverContext,
|
|
1004
|
-
},
|
|
1005
|
-
{
|
|
1006
|
-
// An Angular Console Provider that does not print a set of predefined logs.
|
|
1007
|
-
provide: _Console,
|
|
1008
|
-
// Using `useClass` would necessitate decorating `Console` with `@Injectable`,
|
|
1009
|
-
// which would require switching from `ts_library` to `ng_module`. This change
|
|
1010
|
-
// would also necessitate various patches of `@angular/bazel` to support ESM.
|
|
1011
|
-
useFactory: () => new Console(),
|
|
1012
|
-
},
|
|
1013
|
-
];
|
|
1014
|
-
const isSsrMode = serverContext === ServerRenderContext.SSR;
|
|
1015
|
-
const responseInit = {};
|
|
1221
|
+
const { renderMode = isSsrMode ? RenderMode.Server : RenderMode.Prerender, headers } = matchedRoute;
|
|
1222
|
+
const platformProviders = [];
|
|
1223
|
+
let responseInit;
|
|
1016
1224
|
if (isSsrMode) {
|
|
1225
|
+
// Initialize the response with status and headers if available.
|
|
1226
|
+
responseInit = {
|
|
1227
|
+
status,
|
|
1228
|
+
headers: headers ? new Headers(headers) : undefined,
|
|
1229
|
+
};
|
|
1230
|
+
if (renderMode === RenderMode.Client) {
|
|
1231
|
+
// Serve the client-side rendered version if the route is configured for CSR.
|
|
1232
|
+
return new Response(await this.assets.getServerAsset('index.csr.html'), responseInit);
|
|
1233
|
+
}
|
|
1017
1234
|
platformProviders.push({
|
|
1018
1235
|
provide: REQUEST,
|
|
1019
1236
|
useValue: request,
|
|
@@ -1032,7 +1249,7 @@ class AngularServerApp {
|
|
|
1032
1249
|
html = await hooks.run('html:transform:pre', { html });
|
|
1033
1250
|
}
|
|
1034
1251
|
this.boostrap ??= await manifest.bootstrap();
|
|
1035
|
-
html = await renderAngular(html, this.boostrap, new URL(request.url), platformProviders);
|
|
1252
|
+
html = await renderAngular(html, this.boostrap, new URL(request.url), platformProviders, SERVER_CONTEXT_VALUE[renderMode]);
|
|
1036
1253
|
if (manifest.inlineCriticalCss) {
|
|
1037
1254
|
// Optionally inline critical CSS.
|
|
1038
1255
|
this.inlineCriticalCssProcessor ??= new InlineCriticalCssProcessor((path) => {
|
|
@@ -1199,7 +1416,7 @@ class AngularAppEngine {
|
|
|
1199
1416
|
* @returns A `Map` containing the HTTP headers as key-value pairs.
|
|
1200
1417
|
* @note This function should be used exclusively for retrieving headers of SSG pages.
|
|
1201
1418
|
*/
|
|
1202
|
-
|
|
1419
|
+
getPrerenderHeaders(request) {
|
|
1203
1420
|
if (this.manifest.staticPathsHeaders.size === 0) {
|
|
1204
1421
|
return new Map();
|
|
1205
1422
|
}
|
|
@@ -1209,5 +1426,5 @@ class AngularAppEngine {
|
|
|
1209
1426
|
}
|
|
1210
1427
|
}
|
|
1211
1428
|
|
|
1212
|
-
export { AngularAppEngine, InlineCriticalCssProcessor as ɵInlineCriticalCssProcessor,
|
|
1429
|
+
export { AngularAppEngine, provideServerRoutesConfig, InlineCriticalCssProcessor as ɵInlineCriticalCssProcessor, destroyAngularServerApp as ɵdestroyAngularServerApp, extractRoutesAndCreateRouteTree as ɵextractRoutesAndCreateRouteTree, getOrCreateAngularServerApp as ɵgetOrCreateAngularServerApp, getRoutesFromAngularRouterConfig as ɵgetRoutesFromAngularRouterConfig, setAngularAppEngineManifest as ɵsetAngularAppEngineManifest, setAngularAppManifest as ɵsetAngularAppManifest };
|
|
1213
1430
|
//# sourceMappingURL=ssr.mjs.map
|