Skip to content

Commit 64c5252

Browse files
committed
fix(@angular/ssr): show error when multiple routes are set with RenderMode.AppShell
This change introduces error handling to ensure that when multiple routes are configured with `RenderMode.AppShell`, an error message is displayed. This prevents misconfiguration and enhances clarity in route management.
1 parent 50df631 commit 64c5252

File tree

2 files changed

+46
-10
lines changed

2 files changed

+46
-10
lines changed

packages/angular/ssr/src/routes/ng-routes.ts

+23-9
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ const VALID_REDIRECT_RESPONSE_CODES = new Set([301, 302, 303, 307, 308]);
4545
*/
4646
type ServerConfigRouteTreeAdditionalMetadata = Partial<ServerRoute> & {
4747
/** Indicates if the route has been matched with the Angular router routes. */
48-
matched?: boolean;
48+
presentInClientRouter?: boolean;
4949
};
5050

5151
/**
@@ -134,15 +134,15 @@ async function* traverseRoutesConfig(options: {
134134
continue;
135135
}
136136

137-
matchedMetaData.matched = true;
137+
matchedMetaData.presentInClientRouter = true;
138138
}
139139

140140
const metadata: ServerConfigRouteTreeNodeMetadata = {
141141
...matchedMetaData,
142142
route: currentRoutePath,
143143
};
144144

145-
delete metadata.matched;
145+
delete metadata.presentInClientRouter;
146146

147147
// Handle redirects
148148
if (typeof redirectTo === 'string') {
@@ -246,8 +246,8 @@ async function* handleSSGRoute(
246246
if (!getPrerenderParams) {
247247
yield {
248248
error:
249-
`The '${stripLeadingSlash(currentRoutePath)}' route uses prerendering and includes parameters, but 'getPrerenderParams' is missing. ` +
250-
`Please define 'getPrerenderParams' function for this route in your server routing configuration ` +
249+
`The '${stripLeadingSlash(currentRoutePath)}' route uses prerendering and includes parameters, but 'getPrerenderParams' ` +
250+
`is missing. Please define 'getPrerenderParams' function for this route in your server routing configuration ` +
251251
`or specify a different 'renderMode'.`,
252252
};
253253

@@ -442,24 +442,38 @@ export async function getRoutesFromAngularRouterConfig(
442442
includePrerenderFallbackRoutes,
443443
});
444444

445+
let seenAppShellRoute: string | undefined;
445446
for await (const result of traverseRoutes) {
446447
if ('error' in result) {
447448
errors.push(result.error);
448449
} else {
450+
if (result.renderMode === RenderMode.AppShell) {
451+
if (seenAppShellRoute !== undefined) {
452+
errors.push(
453+
`Error: Both '${seenAppShellRoute}' and '${stripLeadingSlash(result.route)}' routes have ` +
454+
`their 'renderMode' set to 'AppShell'. AppShell renderMode should only be assigned to one route. ` +
455+
`Please review your route configurations to ensure that only one route is set to 'RenderMode.AppShell'.`,
456+
);
457+
}
458+
459+
seenAppShellRoute = stripLeadingSlash(result.route);
460+
}
461+
449462
routesResults.push(result);
450463
}
451464
}
452465

453466
if (serverConfigRouteTree) {
454-
for (const { route, matched } of serverConfigRouteTree.traverse()) {
455-
if (matched || route === '**') {
467+
for (const { route, presentInClientRouter } of serverConfigRouteTree.traverse()) {
468+
if (presentInClientRouter || route === '**') {
456469
// Skip if matched or it's the catch-all route.
457470
continue;
458471
}
459472

460473
errors.push(
461-
`The server route '${route}' does not match any routes defined in the Angular routing configuration. ` +
462-
'Please verify and if unneeded remove this route from the server configuration.',
474+
`The '${route}' server route does not match any routes defined in the Angular ` +
475+
`routing configuration (typically provided as a part of the 'provideRouter' call). ` +
476+
'Please make sure that the mentioned server route is present in the Angular routing configuration.',
463477
);
464478
}
465479
}

packages/angular/ssr/test/routes/ng-routes_spec.ts

+23-1
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ describe('extractRoutesAndCreateRouteTree', () => {
314314

315315
expect(errors).toHaveSize(1);
316316
expect(errors[0]).toContain(
317-
`The server route 'invalid' does not match any routes defined in the Angular routing configuration`,
317+
`The 'invalid' server route does not match any routes defined in the Angular routing configuration`,
318318
);
319319
});
320320

@@ -339,4 +339,26 @@ describe('extractRoutesAndCreateRouteTree', () => {
339339
`The 'invalid' route does not match any route defined in the server routing configuration`,
340340
);
341341
});
342+
343+
it(`should error when 'RenderMode.AppShell' is used on more than one route`, async () => {
344+
setAngularAppTestingManifest(
345+
[
346+
{ path: 'home', component: DummyComponent },
347+
{ path: 'shell', component: DummyComponent },
348+
],
349+
[{ path: '**', renderMode: RenderMode.AppShell }],
350+
);
351+
352+
const { errors } = await extractRoutesAndCreateRouteTree(
353+
url,
354+
/** manifest */ undefined,
355+
/** invokeGetPrerenderParams */ false,
356+
/** includePrerenderFallbackRoutes */ false,
357+
);
358+
359+
expect(errors).toHaveSize(1);
360+
expect(errors[0]).toContain(
361+
`Both 'home' and 'shell' routes have their 'renderMode' set to 'AppShell'.`,
362+
);
363+
});
342364
});

0 commit comments

Comments
 (0)