@@ -206,15 +206,12 @@ async function* handleRoute(options: {
206206 let routeSegments : string [ ] | undefined ;
207207 parentRoutes . set ( metadata . route , {
208208 ...metadata ,
209- redirectTo : resolveRedirectTo (
210- parentRoute ,
211- // Reference the route parameters by their position in the URL (see `buildUrlWithParams`).
212- replaceRedirectParams ( route , redirectTo , ( parameter ) => {
213- routeSegments ??= joinUrlParts ( baseHref , currentRoutePath ) . split ( '/' ) ;
214-
215- return `*${ routeSegments . lastIndexOf ( parameter ) } ` ;
216- } ) ,
217- ) ,
209+ // Reference the route parameters by their position in the URL (see `buildUrlWithParams`).
210+ redirectTo : resolveRedirectTo ( parentRoute , route , redirectTo , ( parameter ) => {
211+ routeSegments ??= joinUrlParts ( baseHref , currentRoutePath ) . split ( '/' ) ;
212+
213+ return `*${ routeSegments . lastIndexOf ( parameter ) } ` ;
214+ } ) ,
218215 relativeRedirect : redirectTo [ 0 ] !== '/' ? true : undefined ,
219216 } ) ;
220217 } else {
@@ -469,15 +466,12 @@ async function* handleSSGRoute(
469466 const redirectTo = typeof route . redirectTo === 'string' ? route . redirectTo : undefined ;
470467 if ( redirectTo !== undefined ) {
471468 let routeSegments : string [ ] | undefined ;
472- meta . redirectTo = resolveRedirectTo (
473- parentRoute ,
474- // Reference the route parameters by their position in the URL (see `buildUrlWithParams`).
475- replaceRedirectParams ( route , redirectTo , ( parameter ) => {
476- routeSegments ??= joinUrlParts ( baseHref , currentRoutePath ) . split ( '/' ) ;
469+ // Reference the route parameters by their position in the URL (see `buildUrlWithParams`).
470+ meta . redirectTo = resolveRedirectTo ( parentRoute , route , redirectTo , ( parameter ) => {
471+ routeSegments ??= joinUrlParts ( baseHref , currentRoutePath ) . split ( '/' ) ;
477472
478- return `*${ routeSegments . lastIndexOf ( parameter ) } ` ;
479- } ) ,
480- ) ;
473+ return `*${ routeSegments . lastIndexOf ( parameter ) } ` ;
474+ } ) ;
481475 meta . relativeRedirect = redirectTo [ 0 ] !== '/' ? true : undefined ;
482476 }
483477
@@ -538,11 +532,9 @@ async function* handleSSGRoute(
538532 ? undefined
539533 : resolveRedirectTo (
540534 parentRoute . replace ( URL_PARAMETER_GLOBAL_REGEXP , replacer ) ,
541- replaceRedirectParams (
542- route ,
543- redirectTo ,
544- ( parameter ) => params [ parameter . slice ( 1 ) ] ,
545- ) ,
535+ route ,
536+ redirectTo ,
537+ ( parameter ) => params [ parameter . slice ( 1 ) ] ,
546538 ) ,
547539 } ;
548540 }
@@ -595,73 +587,51 @@ function handlePrerenderParamsReplacement(
595587}
596588
597589/**
598- * Resolves the `redirectTo` property for a given route.
590+ * Resolves the `redirectTo` property for a given route, replacing any `:id` route parameters
591+ * in its path and resolving relative paths against `parentRoute`.
599592 *
600- * This function processes the `redirectTo` property to ensure that it correctly
601- * resolves relative to the parent route path. If `redirectTo` is an absolute path,
602- * it is returned as is. If it is a relative path, it is resolved based on the parent route path,
603- * and, like in the Angular router, its query and fragment are ignored.
593+ * Like in the Angular router, only whole path segments starting with `:` are parameters, only
594+ * parameters defined in the path of the redirecting route itself can be used, and the query and
595+ * fragment of relative redirects are ignored.
604596 *
605597 * @param parentRoute - The path of the parent route.
606- * @param redirectTo - The target path for redirection.
607- * @returns The resolved redirect path as a string.
608- */
609- function resolveRedirectTo ( parentRoute : string , redirectTo : string ) : string {
610- if ( redirectTo [ 0 ] === '/' ) {
611- // If the redirectTo path is absolute, return it as is.
612- return redirectTo ;
613- }
614-
615- // Match Angular router behavior: replace all segments matched by the route.
616- const [ path ] = redirectTo . split ( / [ ? # ] / , 1 ) ;
617-
618- return joinUrlParts ( parentRoute . replace ( URL_PARAMETER_GLOBAL_REGEXP , '*' ) , path ) ;
619- }
620-
621- /**
622- * Replaces the route parameters, such as `:id`, in the path of `redirectTo`.
623- *
624- * Like in the Angular router, only whole path segments starting with `:` are parameters, and only
625- * the parameters defined in the path of the redirecting route itself can be used.
626- *
627598 * @param route - The redirecting route.
628599 * @param redirectTo - The target path for redirection.
629- * @param resolve - A function that returns the replacement for a parameter, such as `:id`.
630- * @returns The redirect path with its route parameters replaced .
600+ * @param resolveParam - A function that returns the replacement for a parameter, such as `:id`.
601+ * @returns The resolved redirect path as a string .
631602 * @throws If `redirectTo` uses a parameter that is not defined in the path of the route.
632603 */
633- function replaceRedirectParams (
604+ function resolveRedirectTo (
605+ parentRoute : string ,
634606 route : Route ,
635607 redirectTo : string ,
636- resolve : ( parameter : string ) => string ,
608+ resolveParam : ( parameter : string ) => string ,
637609) : string {
638- if ( route . matcher || ! redirectTo . includes ( ':' ) ) {
639- // The parameters of a custom matcher are only known at runtime.
640- return redirectTo ;
641- }
642-
643610 const [ path ] = redirectTo . split ( / [ ? # ] / , 1 ) ;
644- if ( ! path . includes ( ':' ) ) {
645- return redirectTo ;
646- }
647-
648- const routeSegments = route . path ?. split ( '/' ) ?? [ ] ;
649- const resolvedPath = path
650- . split ( '/' )
651- . map ( ( segment ) => {
652- if ( segment [ 0 ] !== ':' ) {
653- return segment ;
654- }
611+ let resolvedPath = path ;
612+
613+ // The parameters of a custom matcher are only known at runtime.
614+ if ( ! route . matcher && path . includes ( ':' ) ) {
615+ const routeSegments = route . path ?. split ( '/' ) ?? [ ] ;
616+ resolvedPath = path
617+ . split ( '/' )
618+ . map ( ( segment ) => {
619+ if ( segment [ 0 ] !== ':' ) {
620+ return segment ;
621+ }
655622
656- if ( ! routeSegments . includes ( segment ) ) {
657- throw new Error ( `Cannot redirect to '${ redirectTo } '. Cannot find '${ segment } '.` ) ;
658- }
623+ if ( ! routeSegments . includes ( segment ) ) {
624+ throw new Error ( `Cannot redirect to '${ redirectTo } '. Cannot find '${ segment } '.` ) ;
625+ }
659626
660- return resolve ( segment ) ;
661- } )
662- . join ( '/' ) ;
627+ return resolveParam ( segment ) ;
628+ } )
629+ . join ( '/' ) ;
630+ }
663631
664- return resolvedPath + redirectTo . slice ( path . length ) ;
632+ return redirectTo [ 0 ] === '/'
633+ ? resolvedPath + redirectTo . slice ( path . length )
634+ : joinUrlParts ( parentRoute . replace ( URL_PARAMETER_GLOBAL_REGEXP , '*' ) , resolvedPath ) ;
665635}
666636
667637/**
@@ -939,12 +909,7 @@ export function extractRoutesAndCreateRouteTree(options: {
939909
940910 for ( const { route, ...metadata } of routes ) {
941911 if ( metadata . redirectTo !== undefined ) {
942- const searchIndex = metadata . redirectTo . search ( / [ ? # ] / ) ;
943- metadata . redirectTo =
944- searchIndex === - 1
945- ? joinUrlParts ( baseHref , metadata . redirectTo )
946- : joinUrlParts ( baseHref , metadata . redirectTo . slice ( 0 , searchIndex ) ) +
947- metadata . redirectTo . slice ( searchIndex ) ;
912+ metadata . redirectTo = joinUrlParts ( baseHref , metadata . redirectTo ) ;
948913 }
949914
950915 // Remove undefined fields
0 commit comments