@@ -31,7 +31,7 @@ import { AngularAppManifest, getAngularAppManifest } from '../manifest';
3131import { AngularBootstrap , isNgModule } from '../utils/ng' ;
3232import { promiseWithAbort } from '../utils/promise' ;
3333import { VALID_REDIRECT_RESPONSE_CODES , isValidRedirectResponseCode } from '../utils/redirect' ;
34- import { joinUrlParts , stripLeadingSlash } from '../utils/url' ;
34+ import { addLeadingSlash , joinUrlParts , stripLeadingSlash } from '../utils/url' ;
3535import {
3636 PrerenderFallback ,
3737 RenderMode ,
@@ -202,7 +202,7 @@ async function* handleRoute(options: {
202202 } else if ( typeof redirectTo === 'string' ) {
203203 parentRoutes . set ( metadata . route , {
204204 ...metadata ,
205- redirectTo : resolveRedirectTo ( parentRoute , redirectTo ) ,
205+ redirectTo : resolveRedirectTo ( parentRoute , redirectTo , currentRoutePath ) ,
206206 relativeRedirect : redirectTo [ 0 ] !== '/' ? true : undefined ,
207207 } ) ;
208208 } else {
@@ -452,7 +452,7 @@ async function* handleSSGRoute(
452452 }
453453
454454 if ( redirectTo !== undefined ) {
455- meta . redirectTo = resolveRedirectTo ( parentRoute , redirectTo ) ;
455+ meta . redirectTo = resolveRedirectTo ( parentRoute , redirectTo , currentRoutePath ) ;
456456 meta . relativeRedirect = redirectTo [ 0 ] !== '/' ? true : undefined ;
457457 }
458458
@@ -513,7 +513,10 @@ async function* handleSSGRoute(
513513 ? undefined
514514 : resolveRedirectTo (
515515 parentRoute . replace ( URL_PARAMETER_GLOBAL_REGEXP , replacer ) ,
516- redirectTo ,
516+ redirectTo . replace ( URL_PARAMETER_GLOBAL_REGEXP , ( match ) =>
517+ Object . hasOwn ( params , match . slice ( 1 ) ) ? replacer ( match ) : match ,
518+ ) ,
519+ currentRoutePath ,
517520 ) ,
518521 } ;
519522 }
@@ -570,23 +573,53 @@ function handlePrerenderParamsReplacement(
570573 *
571574 * This function processes the `redirectTo` property to ensure that it correctly
572575 * resolves relative to the parent route path. If `redirectTo` is an absolute path,
573- * it is returned as is. If it is a relative path, it is resolved based on the parent route path,
576+ * it is used as is. If it is a relative path, it is resolved based on the parent route path,
574577 * and, like in the Angular router, its query and fragment are ignored.
575578 *
579+ * Like in the Angular router, route parameters (e.g. `:id`) used in `redirectTo` take the value
580+ * they have in the matched URL. They are emitted as `*` placeholders, or `*<n>` when the value
581+ * comes from a different segment position than the one it is used in.
582+ *
576583 * @param parentRoute - The path of the parent route.
577584 * @param redirectTo - The target path for redirection.
585+ * @param currentRoutePath - The full path of the route that redirects, including its parent route.
578586 * @returns The resolved redirect path as a string.
579587 */
580- function resolveRedirectTo ( parentRoute : string , redirectTo : string ) : string {
588+ function resolveRedirectTo (
589+ parentRoute : string ,
590+ redirectTo : string ,
591+ currentRoutePath : string ,
592+ ) : string {
593+ let target : string ;
581594 if ( redirectTo [ 0 ] === '/' ) {
582- // If the redirectTo path is absolute, return it as is.
583- return redirectTo ;
595+ target = redirectTo ;
596+ } else {
597+ // Match Angular router behavior: replace all segments matched by the route.
598+ const [ path ] = redirectTo . split ( / [ ? # ] / , 1 ) ;
599+ target = joinUrlParts ( parentRoute . replace ( URL_PARAMETER_GLOBAL_REGEXP , '*' ) , path ) ;
600+ }
601+
602+ if ( ! URL_PARAMETER_REGEXP . test ( target ) ) {
603+ return target ;
584604 }
585605
586- // Match Angular router behavior: replace all segments matched by the route.
587- const [ path ] = redirectTo . split ( / [ ? # ] / , 1 ) ;
606+ const sourceSegments = addLeadingSlash ( currentRoutePath ) . split ( '/' ) ;
607+
608+ return target
609+ . split ( '/' )
610+ . map ( ( segment , index ) => {
611+ if ( segment [ 0 ] !== ':' ) {
612+ return segment ;
613+ }
614+
615+ const sourceIndex = sourceSegments . indexOf ( segment ) ;
616+ if ( sourceIndex === - 1 ) {
617+ return segment ;
618+ }
588619
589- return joinUrlParts ( parentRoute . replace ( URL_PARAMETER_GLOBAL_REGEXP , '*' ) , path ) ;
620+ return sourceIndex === index ? '*' : `*${ sourceIndex } ` ;
621+ } )
622+ . join ( '/' ) ;
590623}
591624
592625/**
0 commit comments