@angular/ssr 19.0.0-next.6 → 19.0.0-next.7
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 +24 -3
- package/fesm2022/node.mjs.map +1 -1
- package/fesm2022/ssr.mjs +211 -110
- package/fesm2022/ssr.mjs.map +1 -1
- package/index.d.ts +25 -8
- package/node/index.d.ts +20 -1
- package/package.json +7 -7
- package/third_party/critters/index.js +3 -1
- package/third_party/critters/index.js.map +2 -2
package/fesm2022/ssr.mjs
CHANGED
|
@@ -134,11 +134,47 @@ function getAngularAppEngineManifest() {
|
|
|
134
134
|
* ```js
|
|
135
135
|
* stripTrailingSlash('path/'); // 'path'
|
|
136
136
|
* stripTrailingSlash('/path'); // '/path'
|
|
137
|
+
* stripTrailingSlash('/'); // '/'
|
|
138
|
+
* stripTrailingSlash(''); // ''
|
|
137
139
|
* ```
|
|
138
140
|
*/
|
|
139
141
|
function stripTrailingSlash(url) {
|
|
140
142
|
// Check if the last character of the URL is a slash
|
|
141
|
-
return url[url.length - 1] === '/' ? url.slice(0, -1) : url;
|
|
143
|
+
return url.length > 1 && url[url.length - 1] === '/' ? url.slice(0, -1) : url;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Removes the leading slash from a URL if it exists.
|
|
147
|
+
*
|
|
148
|
+
* @param url - The URL string from which to remove the leading slash.
|
|
149
|
+
* @returns The URL string without a leading slash.
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* ```js
|
|
153
|
+
* stripLeadingSlash('/path'); // 'path'
|
|
154
|
+
* stripLeadingSlash('/path/'); // 'path/'
|
|
155
|
+
* stripLeadingSlash('/'); // '/'
|
|
156
|
+
* stripLeadingSlash(''); // ''
|
|
157
|
+
* ```
|
|
158
|
+
*/
|
|
159
|
+
function stripLeadingSlash(url) {
|
|
160
|
+
// Check if the first character of the URL is a slash
|
|
161
|
+
return url.length > 1 && url[0] === '/' ? url.slice(1) : url;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Adds a leading slash to a URL if it does not already have one.
|
|
165
|
+
*
|
|
166
|
+
* @param url - The URL string to which the leading slash will be added.
|
|
167
|
+
* @returns The URL string with a leading slash.
|
|
168
|
+
*
|
|
169
|
+
* @example
|
|
170
|
+
* ```js
|
|
171
|
+
* addLeadingSlash('path'); // '/path'
|
|
172
|
+
* addLeadingSlash('/path'); // '/path'
|
|
173
|
+
* ```
|
|
174
|
+
*/
|
|
175
|
+
function addLeadingSlash(url) {
|
|
176
|
+
// Check if the URL already starts with a slash
|
|
177
|
+
return url[0] === '/' ? url : `/${url}`;
|
|
142
178
|
}
|
|
143
179
|
/**
|
|
144
180
|
* Joins URL parts into a single URL string.
|
|
@@ -153,11 +189,11 @@ function stripTrailingSlash(url) {
|
|
|
153
189
|
* ```js
|
|
154
190
|
* joinUrlParts('path/', '/to/resource'); // '/path/to/resource'
|
|
155
191
|
* joinUrlParts('/path/', 'to/resource'); // '/path/to/resource'
|
|
192
|
+
* joinUrlParts('', ''); // '/'
|
|
156
193
|
* ```
|
|
157
194
|
*/
|
|
158
195
|
function joinUrlParts(...parts) {
|
|
159
|
-
|
|
160
|
-
const normalizeParts = [''];
|
|
196
|
+
const normalizeParts = [];
|
|
161
197
|
for (const part of parts) {
|
|
162
198
|
if (part === '') {
|
|
163
199
|
// Skip any empty parts
|
|
@@ -174,7 +210,7 @@ function joinUrlParts(...parts) {
|
|
|
174
210
|
normalizeParts.push(normalizedPart);
|
|
175
211
|
}
|
|
176
212
|
}
|
|
177
|
-
return normalizeParts.join('/');
|
|
213
|
+
return addLeadingSlash(normalizeParts.join('/'));
|
|
178
214
|
}
|
|
179
215
|
/**
|
|
180
216
|
* Strips `/index.html` from the end of a URL's path, if present.
|
|
@@ -354,8 +390,7 @@ class RouteTree {
|
|
|
354
390
|
*/
|
|
355
391
|
insert(route, metadata) {
|
|
356
392
|
let node = this.root;
|
|
357
|
-
const
|
|
358
|
-
const segments = normalizedRoute.split('/');
|
|
393
|
+
const segments = this.getPathSegments(route);
|
|
359
394
|
for (const segment of segments) {
|
|
360
395
|
// Replace parameterized segments (e.g., :id) with a wildcard (*) for matching
|
|
361
396
|
const normalizedSegment = segment[0] === ':' ? '*' : segment;
|
|
@@ -369,7 +404,7 @@ class RouteTree {
|
|
|
369
404
|
// At the leaf node, store the full route and its associated metadata
|
|
370
405
|
node.metadata = {
|
|
371
406
|
...metadata,
|
|
372
|
-
route:
|
|
407
|
+
route: segments.join('/'),
|
|
373
408
|
};
|
|
374
409
|
node.insertionIndex = this.insertionIndexCounter++;
|
|
375
410
|
}
|
|
@@ -382,7 +417,7 @@ class RouteTree {
|
|
|
382
417
|
* @returns The metadata of the best matching route or `undefined` if no match is found.
|
|
383
418
|
*/
|
|
384
419
|
match(route) {
|
|
385
|
-
const segments =
|
|
420
|
+
const segments = this.getPathSegments(route);
|
|
386
421
|
return this.traverseBySegments(segments)?.metadata;
|
|
387
422
|
}
|
|
388
423
|
/**
|
|
@@ -427,6 +462,15 @@ class RouteTree {
|
|
|
427
462
|
yield* this.traverse(childNode);
|
|
428
463
|
}
|
|
429
464
|
}
|
|
465
|
+
/**
|
|
466
|
+
* Extracts the path segments from a given route string.
|
|
467
|
+
*
|
|
468
|
+
* @param route - The route string from which to extract segments.
|
|
469
|
+
* @returns An array of path segments.
|
|
470
|
+
*/
|
|
471
|
+
getPathSegments(route) {
|
|
472
|
+
return stripTrailingSlash(route).split('/');
|
|
473
|
+
}
|
|
430
474
|
/**
|
|
431
475
|
* Recursively traverses the route tree from a given node, attempting to match the remaining route segments.
|
|
432
476
|
* If the node is a leaf node (no more segments to match) and contains metadata, the node is yielded.
|
|
@@ -515,88 +559,87 @@ const VALID_REDIRECT_RESPONSE_CODES = new Set([301, 302, 303, 307, 308]);
|
|
|
515
559
|
* and lazy-loaded routes. It yields route metadata for each route and its potential variants.
|
|
516
560
|
*
|
|
517
561
|
* @param options - The configuration options for traversing routes.
|
|
518
|
-
* @returns An async iterable iterator
|
|
562
|
+
* @returns An async iterable iterator yielding either route tree node metadata or an error object with an error message.
|
|
519
563
|
*/
|
|
520
|
-
async function* traverseRoutesConfig(
|
|
564
|
+
async function* traverseRoutesConfig(options) {
|
|
565
|
+
const { routes, compiler, parentInjector, parentRoute, serverConfigRouteTree, invokeGetPrerenderParams, includePrerenderFallbackRoutes, } = options;
|
|
521
566
|
for (const route of routes) {
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
`Please use one of the following redirect response codes: ${[...VALID_REDIRECT_RESPONSE_CODES.values()].join(', ')}.`);
|
|
567
|
+
try {
|
|
568
|
+
const { path = '', redirectTo, loadChildren, children } = route;
|
|
569
|
+
const currentRoutePath = joinUrlParts(parentRoute, path);
|
|
570
|
+
// Get route metadata from the server config route tree, if available
|
|
571
|
+
let matchedMetaData;
|
|
572
|
+
if (serverConfigRouteTree) {
|
|
573
|
+
matchedMetaData = serverConfigRouteTree.match(currentRoutePath);
|
|
574
|
+
if (!matchedMetaData) {
|
|
575
|
+
yield {
|
|
576
|
+
error: `The '${currentRoutePath}' route does not match any route defined in the server routing configuration. ` +
|
|
577
|
+
'Please ensure this route is added to the server routing configuration.',
|
|
578
|
+
};
|
|
579
|
+
continue;
|
|
580
|
+
}
|
|
537
581
|
}
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
582
|
+
const metadata = {
|
|
583
|
+
...matchedMetaData,
|
|
584
|
+
route: currentRoutePath,
|
|
585
|
+
};
|
|
586
|
+
// Handle redirects
|
|
587
|
+
if (typeof redirectTo === 'string') {
|
|
588
|
+
const redirectToResolved = resolveRedirectTo(currentRoutePath, redirectTo);
|
|
589
|
+
if (metadata.status && !VALID_REDIRECT_RESPONSE_CODES.has(metadata.status)) {
|
|
590
|
+
yield {
|
|
591
|
+
error: `The '${metadata.status}' status code is not a valid redirect response code. ` +
|
|
592
|
+
`Please use one of the following redirect response codes: ${[...VALID_REDIRECT_RESPONSE_CODES.values()].join(', ')}.`,
|
|
593
|
+
};
|
|
594
|
+
continue;
|
|
595
|
+
}
|
|
596
|
+
yield { ...metadata, redirectTo: redirectToResolved };
|
|
597
|
+
}
|
|
598
|
+
else if (metadata.renderMode === RenderMode.Prerender) {
|
|
599
|
+
// Handle SSG routes
|
|
600
|
+
yield* handleSSGRoute(metadata, parentInjector, invokeGetPrerenderParams, includePrerenderFallbackRoutes);
|
|
601
|
+
}
|
|
602
|
+
else {
|
|
603
|
+
yield metadata;
|
|
604
|
+
}
|
|
605
|
+
// Recursively process child routes
|
|
606
|
+
if (children?.length) {
|
|
563
607
|
yield* traverseRoutesConfig({
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
parentInjector: injector,
|
|
608
|
+
...options,
|
|
609
|
+
routes: children,
|
|
567
610
|
parentRoute: currentRoutePath,
|
|
568
|
-
serverConfigRouteTree,
|
|
569
|
-
invokeGetPrerenderParams,
|
|
570
611
|
});
|
|
571
612
|
}
|
|
613
|
+
// Load and process lazy-loaded child routes
|
|
614
|
+
if (loadChildren) {
|
|
615
|
+
const loadedChildRoutes = await _loadChildren(route, compiler, parentInjector).toPromise();
|
|
616
|
+
if (loadedChildRoutes) {
|
|
617
|
+
const { routes: childRoutes, injector = parentInjector } = loadedChildRoutes;
|
|
618
|
+
yield* traverseRoutesConfig({
|
|
619
|
+
...options,
|
|
620
|
+
routes: childRoutes,
|
|
621
|
+
parentInjector: injector,
|
|
622
|
+
parentRoute: currentRoutePath,
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
catch (error) {
|
|
628
|
+
yield { error: `Error processing route '${route.path}': ${error.message}` };
|
|
572
629
|
}
|
|
573
630
|
}
|
|
574
631
|
}
|
|
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
632
|
/**
|
|
591
633
|
* Handles SSG (Static Site Generation) routes by invoking `getPrerenderParams` and yielding
|
|
592
|
-
* all parameterized paths.
|
|
634
|
+
* all parameterized paths, returning any errors encountered.
|
|
593
635
|
*
|
|
594
636
|
* @param metadata - The metadata associated with the route tree node.
|
|
595
637
|
* @param parentInjector - The dependency injection container for the parent route.
|
|
596
638
|
* @param invokeGetPrerenderParams - A flag indicating whether to invoke the `getPrerenderParams` function.
|
|
597
|
-
* @
|
|
639
|
+
* @param includePrerenderFallbackRoutes - A flag indicating whether to include fallback routes in the result.
|
|
640
|
+
* @returns An async iterable iterator that yields route tree node metadata for each SSG path or errors.
|
|
598
641
|
*/
|
|
599
|
-
async function* handleSSGRoute(metadata, parentInjector, invokeGetPrerenderParams) {
|
|
642
|
+
async function* handleSSGRoute(metadata, parentInjector, invokeGetPrerenderParams, includePrerenderFallbackRoutes) {
|
|
600
643
|
if (metadata.renderMode !== RenderMode.Prerender) {
|
|
601
644
|
throw new Error(`'handleSSGRoute' was called for a route which rendering mode is not prerender.`);
|
|
602
645
|
}
|
|
@@ -605,30 +648,48 @@ async function* handleSSGRoute(metadata, parentInjector, invokeGetPrerenderParam
|
|
|
605
648
|
if ('getPrerenderParams' in meta) {
|
|
606
649
|
delete meta['getPrerenderParams'];
|
|
607
650
|
}
|
|
608
|
-
if (
|
|
651
|
+
if (!URL_PARAMETER_REGEXP.test(currentRoutePath)) {
|
|
652
|
+
// Route has no parameters
|
|
653
|
+
yield {
|
|
654
|
+
...meta,
|
|
655
|
+
route: currentRoutePath,
|
|
656
|
+
};
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
if (invokeGetPrerenderParams) {
|
|
609
660
|
if (!getPrerenderParams) {
|
|
610
|
-
|
|
611
|
-
`
|
|
612
|
-
|
|
661
|
+
yield {
|
|
662
|
+
error: `The '${currentRoutePath}' route uses prerendering and includes parameters, but 'getPrerenderParams' is missing. ` +
|
|
663
|
+
`Please define 'getPrerenderParams' function for this route in your server routing configuration ` +
|
|
664
|
+
`or specify a different 'renderMode'.`,
|
|
665
|
+
};
|
|
666
|
+
return;
|
|
613
667
|
}
|
|
614
668
|
const parameters = await runInInjectionContext(parentInjector, () => getPrerenderParams());
|
|
615
|
-
|
|
616
|
-
const
|
|
617
|
-
const
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
`
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
669
|
+
try {
|
|
670
|
+
for (const params of parameters) {
|
|
671
|
+
const routeWithResolvedParams = currentRoutePath.replace(URL_PARAMETER_REGEXP, (match) => {
|
|
672
|
+
const parameterName = match.slice(1);
|
|
673
|
+
const value = params[parameterName];
|
|
674
|
+
if (typeof value !== 'string') {
|
|
675
|
+
throw new Error(`The 'getPrerenderParams' function defined for the '${currentRoutePath}' route ` +
|
|
676
|
+
`returned a non-string value for parameter '${parameterName}'. ` +
|
|
677
|
+
`Please make sure the 'getPrerenderParams' function returns values for all parameters ` +
|
|
678
|
+
'specified in this route.');
|
|
679
|
+
}
|
|
680
|
+
return value;
|
|
681
|
+
});
|
|
682
|
+
yield { ...meta, route: routeWithResolvedParams };
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
catch (error) {
|
|
686
|
+
yield { error: `${error.message}` };
|
|
687
|
+
return;
|
|
628
688
|
}
|
|
629
689
|
}
|
|
630
690
|
// Handle fallback render modes
|
|
631
|
-
if (
|
|
691
|
+
if (includePrerenderFallbackRoutes &&
|
|
692
|
+
(fallback !== PrerenderFallback.None || !invokeGetPrerenderParams)) {
|
|
632
693
|
yield {
|
|
633
694
|
...meta,
|
|
634
695
|
route: currentRoutePath,
|
|
@@ -661,21 +722,31 @@ function resolveRedirectTo(routePath, redirectTo) {
|
|
|
661
722
|
* Builds a server configuration route tree from the given server routes configuration.
|
|
662
723
|
*
|
|
663
724
|
* @param serverRoutesConfig - The array of server routes to be used for configuration.
|
|
664
|
-
|
|
725
|
+
|
|
726
|
+
* @returns An object containing:
|
|
727
|
+
* - `serverConfigRouteTree`: A populated `RouteTree` instance, which organizes the server routes
|
|
728
|
+
* along with their additional metadata.
|
|
729
|
+
* - `errors`: An array of strings that list any errors encountered during the route tree construction
|
|
730
|
+
* process, such as invalid paths.
|
|
665
731
|
*/
|
|
666
732
|
function buildServerConfigRouteTree(serverRoutesConfig) {
|
|
667
733
|
const serverConfigRouteTree = new RouteTree();
|
|
734
|
+
const errors = [];
|
|
668
735
|
for (const { path, ...metadata } of serverRoutesConfig) {
|
|
736
|
+
if (path[0] === '/') {
|
|
737
|
+
errors.push(`Invalid '${path}' route configuration: the path cannot start with a slash.`);
|
|
738
|
+
continue;
|
|
739
|
+
}
|
|
669
740
|
serverConfigRouteTree.insert(path, metadata);
|
|
670
741
|
}
|
|
671
|
-
return serverConfigRouteTree;
|
|
742
|
+
return { serverConfigRouteTree, errors };
|
|
672
743
|
}
|
|
673
744
|
/**
|
|
674
745
|
* Retrieves routes from the given Angular application.
|
|
675
746
|
*
|
|
676
747
|
* This function initializes an Angular platform, bootstraps the application or module,
|
|
677
748
|
* and retrieves routes from the Angular router configuration. It handles both module-based
|
|
678
|
-
* and function-based bootstrapping. It yields the resulting routes as `RouteTreeNodeMetadata` objects.
|
|
749
|
+
* and function-based bootstrapping. It yields the resulting routes as `RouteTreeNodeMetadata` objects or errors.
|
|
679
750
|
*
|
|
680
751
|
* @param bootstrap - A function that returns a promise resolving to an `ApplicationRef` or an Angular module to bootstrap.
|
|
681
752
|
* @param document - The initial HTML document used for server-side rendering.
|
|
@@ -684,12 +755,11 @@ function buildServerConfigRouteTree(serverRoutesConfig) {
|
|
|
684
755
|
* for ensuring that API requests for relative paths succeed, which is essential for accurate route extraction.
|
|
685
756
|
* @param invokeGetPrerenderParams - A boolean flag indicating whether to invoke `getPrerenderParams` for parameterized SSG routes
|
|
686
757
|
* to handle prerendering paths. Defaults to `false`.
|
|
687
|
-
*
|
|
688
|
-
*
|
|
689
|
-
*
|
|
690
|
-
* @returns A promise that resolves to an object of type `AngularRouterConfigResult`.
|
|
758
|
+
* @param includePrerenderFallbackRoutes - A flag indicating whether to include fallback routes in the result. Defaults to `true`.
|
|
759
|
+
*
|
|
760
|
+
* @returns A promise that resolves to an object of type `AngularRouterConfigResult` or errors.
|
|
691
761
|
*/
|
|
692
|
-
async function getRoutesFromAngularRouterConfig(bootstrap, document, url, invokeGetPrerenderParams = false) {
|
|
762
|
+
async function getRoutesFromAngularRouterConfig(bootstrap, document, url, invokeGetPrerenderParams = false, includePrerenderFallbackRoutes = true) {
|
|
693
763
|
const { protocol, host } = url;
|
|
694
764
|
// Create and initialize the Angular platform for server-side rendering.
|
|
695
765
|
const platformRef = createPlatformFactory(platformCore, 'server', [
|
|
@@ -717,12 +787,25 @@ async function getRoutesFromAngularRouterConfig(bootstrap, document, url, invoke
|
|
|
717
787
|
const injector = applicationRef.injector;
|
|
718
788
|
const router = injector.get(Router);
|
|
719
789
|
const routesResults = [];
|
|
790
|
+
const errors = [];
|
|
791
|
+
const baseHref = injector.get(APP_BASE_HREF, null, { optional: true }) ??
|
|
792
|
+
injector.get(PlatformLocation).getBaseHrefFromDOM();
|
|
720
793
|
if (router.config.length) {
|
|
721
794
|
const compiler = injector.get(Compiler);
|
|
722
795
|
const serverRoutesConfig = injector.get(SERVER_ROUTES_CONFIG, null, { optional: true });
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
796
|
+
let serverConfigRouteTree;
|
|
797
|
+
if (serverRoutesConfig) {
|
|
798
|
+
const result = buildServerConfigRouteTree(serverRoutesConfig);
|
|
799
|
+
serverConfigRouteTree = result.serverConfigRouteTree;
|
|
800
|
+
errors.push(...result.errors);
|
|
801
|
+
}
|
|
802
|
+
if (errors.length) {
|
|
803
|
+
return {
|
|
804
|
+
baseHref,
|
|
805
|
+
routes: routesResults,
|
|
806
|
+
errors,
|
|
807
|
+
};
|
|
808
|
+
}
|
|
726
809
|
// Retrieve all routes from the Angular router configuration.
|
|
727
810
|
const traverseRoutes = traverseRoutesConfig({
|
|
728
811
|
routes: router.config,
|
|
@@ -731,19 +814,24 @@ async function getRoutesFromAngularRouterConfig(bootstrap, document, url, invoke
|
|
|
731
814
|
parentRoute: '',
|
|
732
815
|
serverConfigRouteTree,
|
|
733
816
|
invokeGetPrerenderParams,
|
|
817
|
+
includePrerenderFallbackRoutes,
|
|
734
818
|
});
|
|
735
819
|
for await (const result of traverseRoutes) {
|
|
736
|
-
|
|
820
|
+
if ('error' in result) {
|
|
821
|
+
errors.push(result.error);
|
|
822
|
+
}
|
|
823
|
+
else {
|
|
824
|
+
routesResults.push(result);
|
|
825
|
+
}
|
|
737
826
|
}
|
|
738
827
|
}
|
|
739
828
|
else {
|
|
740
829
|
routesResults.push({ route: '', renderMode: RenderMode.Prerender });
|
|
741
830
|
}
|
|
742
|
-
const baseHref = injector.get(APP_BASE_HREF, null, { optional: true }) ??
|
|
743
|
-
injector.get(PlatformLocation).getBaseHrefFromDOM();
|
|
744
831
|
return {
|
|
745
832
|
baseHref,
|
|
746
833
|
routes: routesResults,
|
|
834
|
+
errors,
|
|
747
835
|
};
|
|
748
836
|
}
|
|
749
837
|
finally {
|
|
@@ -763,13 +851,17 @@ async function getRoutesFromAngularRouterConfig(bootstrap, document, url, invoke
|
|
|
763
851
|
* If not provided, the default manifest is retrieved using `getAngularAppManifest()`.
|
|
764
852
|
* @param invokeGetPrerenderParams - A boolean flag indicating whether to invoke `getPrerenderParams` for parameterized SSG routes
|
|
765
853
|
* to handle prerendering paths. Defaults to `false`.
|
|
766
|
-
* @
|
|
854
|
+
* @param includePrerenderFallbackRoutes - A flag indicating whether to include fallback routes in the result. Defaults to `true`.
|
|
855
|
+
*
|
|
856
|
+
* @returns A promise that resolves to an object containing:
|
|
857
|
+
* - `routeTree`: A populated `RouteTree` containing all extracted routes from the Angular application.
|
|
858
|
+
* - `errors`: An array of strings representing any errors encountered during the route extraction process.
|
|
767
859
|
*/
|
|
768
|
-
async function extractRoutesAndCreateRouteTree(url, manifest = getAngularAppManifest(), invokeGetPrerenderParams = false) {
|
|
860
|
+
async function extractRoutesAndCreateRouteTree(url, manifest = getAngularAppManifest(), invokeGetPrerenderParams = false, includePrerenderFallbackRoutes = true) {
|
|
769
861
|
const routeTree = new RouteTree();
|
|
770
862
|
const document = await new ServerAssets(manifest).getIndexServerHtml();
|
|
771
863
|
const bootstrap = await manifest.bootstrap();
|
|
772
|
-
const { baseHref, routes } = await getRoutesFromAngularRouterConfig(bootstrap, document, url, invokeGetPrerenderParams);
|
|
864
|
+
const { baseHref, routes, errors } = await getRoutesFromAngularRouterConfig(bootstrap, document, url, invokeGetPrerenderParams, includePrerenderFallbackRoutes);
|
|
773
865
|
for (const { route, ...metadata } of routes) {
|
|
774
866
|
if (metadata.redirectTo !== undefined) {
|
|
775
867
|
metadata.redirectTo = joinUrlParts(baseHref, metadata.redirectTo);
|
|
@@ -777,7 +869,10 @@ async function extractRoutesAndCreateRouteTree(url, manifest = getAngularAppMani
|
|
|
777
869
|
const fullRoute = joinUrlParts(baseHref, route);
|
|
778
870
|
routeTree.insert(fullRoute, metadata);
|
|
779
871
|
}
|
|
780
|
-
return
|
|
872
|
+
return {
|
|
873
|
+
routeTree,
|
|
874
|
+
errors,
|
|
875
|
+
};
|
|
781
876
|
}
|
|
782
877
|
|
|
783
878
|
/**
|
|
@@ -912,7 +1007,13 @@ class ServerRouter {
|
|
|
912
1007
|
// Create and store a new promise for the build process.
|
|
913
1008
|
// This prevents concurrent builds by re-using the same promise.
|
|
914
1009
|
ServerRouter.#extractionPromise ??= extractRoutesAndCreateRouteTree(url, manifest)
|
|
915
|
-
.then((routeTree) =>
|
|
1010
|
+
.then(({ routeTree, errors }) => {
|
|
1011
|
+
if (errors.length > 0) {
|
|
1012
|
+
throw new Error('Error(s) occurred while extracting routes:\n' +
|
|
1013
|
+
errors.map((error) => `- ${error}`).join('\n'));
|
|
1014
|
+
}
|
|
1015
|
+
return new ServerRouter(routeTree);
|
|
1016
|
+
})
|
|
916
1017
|
.finally(() => {
|
|
917
1018
|
ServerRouter.#extractionPromise = undefined;
|
|
918
1019
|
});
|