diff --git a/packages/angular/ssr/src/routes/ng-routes.ts b/packages/angular/ssr/src/routes/ng-routes.ts index 2a9281f40e9b..e46dd685511a 100644 --- a/packages/angular/ssr/src/routes/ng-routes.ts +++ b/packages/angular/ssr/src/routes/ng-routes.ts @@ -251,15 +251,22 @@ async function* traverseRoutesConfig(options: { const currentRoutePath = joinUrlParts(parentRoute, path); if (matcher && serverConfigRouteTree) { - let foundMatch = false; + const matches: (RouteTreeNodeMetadata & ServerConfigRouteTreeAdditionalMetadata)[] = []; for (const matchedMetaData of serverConfigRouteTree.traverse()) { - if (!matchedMetaData.route.startsWith(currentRoutePath)) { - continue; + if (matchedMetaData.route.startsWith(currentRoutePath)) { + matches.push(matchedMetaData); } + } - foundMatch = true; - matchedMetaData.presentInClientRouter = true; + if (!matches.length) { + const matchedMetaData = serverConfigRouteTree.match(currentRoutePath); + if (matchedMetaData) { + matches.push(matchedMetaData); + } + } + for (const matchedMetaData of matches) { + matchedMetaData.presentInClientRouter = true; if (matchedMetaData.renderMode === RenderMode.Prerender) { yield { error: @@ -282,7 +289,7 @@ async function* traverseRoutesConfig(options: { }); } - if (!foundMatch) { + if (!matches.length) { yield { error: `The route '${stripLeadingSlash(currentRoutePath)}' has a defined matcher but does not ` + diff --git a/packages/angular/ssr/test/routes/ng-routes_spec.ts b/packages/angular/ssr/test/routes/ng-routes_spec.ts index 628546432d0c..324abe8c4d29 100644 --- a/packages/angular/ssr/test/routes/ng-routes_spec.ts +++ b/packages/angular/ssr/test/routes/ng-routes_spec.ts @@ -428,6 +428,45 @@ describe('extractRoutesAndCreateRouteTree', () => { ]); }); + it('should extract routes with a route level matcher captured by "**"', async () => { + setAngularAppTestingManifest( + [ + { + path: '', + component: DummyComponent, + }, + { + path: 'list', + component: DummyComponent, + }, + { + path: 'product', + component: DummyComponent, + children: [ + { + matcher: () => null, + component: DummyComponent, + }, + ], + }, + ], + [ + { path: 'list', renderMode: RenderMode.Client }, + { path: '', renderMode: RenderMode.Client }, + { path: '**', renderMode: RenderMode.Server }, + ], + ); + + const { routeTree, errors } = await extractRoutesAndCreateRouteTree({ url }); + expect(errors).toHaveSize(0); + expect(routeTree.toObject()).toEqual([ + { route: '/', renderMode: RenderMode.Client }, + { route: '/list', renderMode: RenderMode.Client }, + { route: '/product', renderMode: RenderMode.Server }, + { route: '/**', renderMode: RenderMode.Server }, + ]); + }); + it('should extract nested redirects that are not explicitly defined.', async () => { setAngularAppTestingManifest( [