Skip to content

Commit 4eb22bc

Browse files
committed
fix(@angular/ssr): improve route matching for wildcard routes
Enhance the route traversal logic to correctly identify and process wildcard route matchers (e.g., '**'). This ensures that routes with matchers are properly marked as present in the client router and handled according to their render mode. closes #31666
1 parent 30efc56 commit 4eb22bc

File tree

2 files changed

+52
-6
lines changed

2 files changed

+52
-6
lines changed

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

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -251,15 +251,22 @@ async function* traverseRoutesConfig(options: {
251251
const currentRoutePath = joinUrlParts(parentRoute, path);
252252

253253
if (matcher && serverConfigRouteTree) {
254-
let foundMatch = false;
254+
const matches: (RouteTreeNodeMetadata & ServerConfigRouteTreeAdditionalMetadata)[] = [];
255255
for (const matchedMetaData of serverConfigRouteTree.traverse()) {
256-
if (!matchedMetaData.route.startsWith(currentRoutePath)) {
257-
continue;
256+
if (matchedMetaData.route.startsWith(currentRoutePath)) {
257+
matches.push(matchedMetaData);
258258
}
259+
}
259260

260-
foundMatch = true;
261-
matchedMetaData.presentInClientRouter = true;
261+
if (!matches.length) {
262+
const matchedMetaData = serverConfigRouteTree.match(currentRoutePath);
263+
if (matchedMetaData) {
264+
matches.push(matchedMetaData);
265+
}
266+
}
262267

268+
for (const matchedMetaData of matches) {
269+
matchedMetaData.presentInClientRouter = true;
263270
if (matchedMetaData.renderMode === RenderMode.Prerender) {
264271
yield {
265272
error:
@@ -282,7 +289,7 @@ async function* traverseRoutesConfig(options: {
282289
});
283290
}
284291

285-
if (!foundMatch) {
292+
if (!matches.length) {
286293
yield {
287294
error:
288295
`The route '${stripLeadingSlash(currentRoutePath)}' has a defined matcher but does not ` +

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

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,45 @@ describe('extractRoutesAndCreateRouteTree', () => {
428428
]);
429429
});
430430

431+
it('should extract routes with a route level matcher captured by "**"', async () => {
432+
setAngularAppTestingManifest(
433+
[
434+
{
435+
path: '',
436+
component: DummyComponent,
437+
},
438+
{
439+
path: 'list',
440+
component: DummyComponent,
441+
},
442+
{
443+
path: 'product',
444+
component: DummyComponent,
445+
children: [
446+
{
447+
matcher: () => null,
448+
component: DummyComponent,
449+
},
450+
],
451+
},
452+
],
453+
[
454+
{ path: 'list', renderMode: RenderMode.Client },
455+
{ path: '', renderMode: RenderMode.Client },
456+
{ path: '**', renderMode: RenderMode.Server },
457+
],
458+
);
459+
460+
const { routeTree, errors } = await extractRoutesAndCreateRouteTree({ url });
461+
expect(errors).toHaveSize(0);
462+
expect(routeTree.toObject()).toEqual([
463+
{ route: '/', renderMode: RenderMode.Client },
464+
{ route: '/list', renderMode: RenderMode.Client },
465+
{ route: '/product', renderMode: RenderMode.Server },
466+
{ route: '/**', renderMode: RenderMode.Server },
467+
]);
468+
});
469+
431470
it('should extract nested redirects that are not explicitly defined.', async () => {
432471
setAngularAppTestingManifest(
433472
[

0 commit comments

Comments
 (0)