diff --git a/packages/react-router/src/link.tsx b/packages/react-router/src/link.tsx index 9ec0835c0b..c90be7f361 100644 --- a/packages/react-router/src/link.tsx +++ b/packages/react-router/src/link.tsx @@ -71,7 +71,14 @@ export type Split = S extends unknown : never export type ParsePathParams = keyof { - [K in Trim[number], '_'> as K extends `$${infer L}` ? L : never]: K + [K in Trim< + Split[number], + '_' + > as K extends `${string}$${infer L}$${string}` + ? L + : K extends `${string}$${infer L}` + ? L + : never]: K } export type Join = T extends [] diff --git a/packages/react-router/src/path.ts b/packages/react-router/src/path.ts index fd4450ef4a..77e6cde72b 100644 --- a/packages/react-router/src/path.ts +++ b/packages/react-router/src/path.ts @@ -97,7 +97,7 @@ export function parsePathname(pathname?: string): Segment[] { } } - if (part.charAt(0) === '$') { + if (part.includes('$')) { return { type: 'param', value: part, @@ -138,7 +138,8 @@ export function interpolatePath( } if (segment.type === 'param') { - return params![segment.value.substring(1)] ?? 'undefined' + const [p, v, ...s] = segment.value.split('$') + return `${p!}${params![v!] ?? ''}${s.join('$')}` } return segment.value @@ -242,8 +243,20 @@ export function matchByPath( if (baseSegment?.value === '/') { return false } - if (baseSegment.value.charAt(0) !== '$') { - params[routeSegment.value.substring(1)] = baseSegment.value + + const [p, v, ...suf] = routeSegment.value.split('$') + const s = suf.join('$') + const staticMatch = matchLocation.caseSensitive + ? baseSegment.value.startsWith(p!) && baseSegment.value.endsWith(s) + : baseSegment.value.toLowerCase().startsWith(p!.toLowerCase()) && + baseSegment.value.toLowerCase().endsWith(s.toLowerCase()) + if (!staticMatch) { + return false + } + + const paramValue = baseSegment.value.slice(p!.length, -s.length) + if (!paramValue.startsWith('$')) { + params[v!] = paramValue } } } diff --git a/packages/react-router/src/utils.ts b/packages/react-router/src/utils.ts index 9ca840d17f..e98e80d15d 100644 --- a/packages/react-router/src/utils.ts +++ b/packages/react-router/src/utils.ts @@ -108,12 +108,17 @@ export type DeepAwaited = T extends Promise ? { [K in A]: DeepAwaited } : T +type PathParamMaskSegment = + S extends `${infer P}$${string}$${infer S}` + ? `${P}${string}${S}` + : S extends `${infer P}$${string}` + ? `${P}${string}` + : S + export type PathParamMask = - TRoutePath extends `${infer L}/$${infer C}/${infer R}` - ? PathParamMask<`${L}/${string}/${R}`> - : TRoutePath extends `${infer L}/$${infer C}` - ? PathParamMask<`${L}/${string}`> - : TRoutePath + TRoutePath extends `${infer T}/${infer U}` + ? `${PathParamMaskSegment}/${PathParamMask}` + : PathParamMaskSegment export type Timeout = ReturnType