From 29297ecc7c56db1f7e9258be6282cc2c91869880 Mon Sep 17 00:00:00 2001 From: Alan Agius <17563226+alan-agius4@users.noreply.github.com> Date: Mon, 6 Jul 2026 12:35:06 +0000 Subject: [PATCH] fix(@angular/ssr): replace all route parameters when resolving relative redirects When resolving a relative `redirectTo` property for a route with multiple path parameters (e.g. `:param1/:param2`), only the first parameter segment was being converted to `*` because a non-global regular expression was used. This commit updates `resolveRedirectTo` to use `URL_PARAMETER_GLOBAL_REGEXP` so that all parameter placeholders in the route path are properly replaced with `*`. Fixes #33504 --- packages/angular/ssr/src/routes/ng-routes.ts | 2 +- .../angular/ssr/test/routes/ng-routes_spec.ts | 29 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/packages/angular/ssr/src/routes/ng-routes.ts b/packages/angular/ssr/src/routes/ng-routes.ts index 04f67c96d9d5..ac83aae8cd4f 100644 --- a/packages/angular/ssr/src/routes/ng-routes.ts +++ b/packages/angular/ssr/src/routes/ng-routes.ts @@ -547,7 +547,7 @@ function resolveRedirectTo(routePath: string, redirectTo: string): string { } // Resolve relative redirectTo based on the current route path. - const segments = routePath.replace(URL_PARAMETER_REGEXP, '*').split('/'); + const segments = routePath.replace(URL_PARAMETER_GLOBAL_REGEXP, '*').split('/'); segments.pop(); // Remove the last segment to make it relative. return joinUrlParts(...segments, redirectTo); diff --git a/packages/angular/ssr/test/routes/ng-routes_spec.ts b/packages/angular/ssr/test/routes/ng-routes_spec.ts index 1532eb337faa..e214c31eab71 100644 --- a/packages/angular/ssr/test/routes/ng-routes_spec.ts +++ b/packages/angular/ssr/test/routes/ng-routes_spec.ts @@ -502,6 +502,35 @@ describe('extractRoutesAndCreateRouteTree', () => { ]); }); + it('should extract nested redirects with multiple path parameters', async () => { + setAngularAppTestingManifest( + [ + { + path: ':param1/:param2', + children: [ + { + path: '', + pathMatch: 'full', + redirectTo: 'thing', + }, + { + path: 'thing', + component: DummyComponent, + }, + ], + }, + ], + [{ path: '**', renderMode: RenderMode.Server }], + ); + + const { routeTree, errors } = await extractRoutesAndCreateRouteTree({ url }); + expect(errors).toHaveSize(0); + expect(routeTree.toObject()).toEqual([ + { route: '/*/*', renderMode: RenderMode.Server, redirectTo: '/*/*/thing' }, + { route: '/*/*/thing', renderMode: RenderMode.Server }, + ]); + }); + it('should not resolve parameterized routes for SSG when `invokeGetPrerenderParams` is false', async () => { setAngularAppTestingManifest( [