| | import type { DynamicParam } from '../../../../server/app-render/app-render' |
| | import type { LoaderTree } from '../../../../server/lib/app-dir-module' |
| | import type { OpaqueFallbackRouteParams } from '../../../../server/request/fallback-params' |
| | import type { Params } from '../../../../server/request/params' |
| | import type { DynamicParamTypesShort } from '../../app-router-types' |
| | import { InvariantError } from '../../invariant-error' |
| | import { parseLoaderTree } from './parse-loader-tree' |
| | import { parseAppRoute, parseAppRouteSegment } from '../routes/app' |
| | import { resolveParamValue } from './resolve-param-value' |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | function getParamValue( |
| | interpolatedParams: Params, |
| | segmentKey: string, |
| | fallbackRouteParams: OpaqueFallbackRouteParams | null |
| | ) { |
| | let value = interpolatedParams[segmentKey] |
| |
|
| | if (fallbackRouteParams?.has(segmentKey)) { |
| | |
| | |
| | const [searchValue] = fallbackRouteParams.get(segmentKey)! |
| | value = searchValue |
| | } else if (Array.isArray(value)) { |
| | value = value.map((i) => encodeURIComponent(i)) |
| | } else if (typeof value === 'string') { |
| | value = encodeURIComponent(value) |
| | } |
| |
|
| | return value |
| | } |
| |
|
| | export function interpolateParallelRouteParams( |
| | loaderTree: LoaderTree, |
| | params: Params, |
| | pagePath: string, |
| | fallbackRouteParams: OpaqueFallbackRouteParams | null |
| | ): Params { |
| | const interpolated = structuredClone(params) |
| |
|
| | |
| | const stack: Array<{ tree: LoaderTree; depth: number }> = [ |
| | { tree: loaderTree, depth: 0 }, |
| | ] |
| |
|
| | |
| | const route = parseAppRoute(pagePath, true) |
| |
|
| | while (stack.length > 0) { |
| | const { tree, depth } = stack.pop()! |
| | const { segment, parallelRoutes } = parseLoaderTree(tree) |
| |
|
| | const appSegment = parseAppRouteSegment(segment) |
| |
|
| | if ( |
| | appSegment?.type === 'dynamic' && |
| | !interpolated.hasOwnProperty(appSegment.param.paramName) && |
| | |
| | |
| | !fallbackRouteParams?.has(appSegment.param.paramName) |
| | ) { |
| | const { paramName, paramType } = appSegment.param |
| |
|
| | const paramValue = resolveParamValue( |
| | paramName, |
| | paramType, |
| | depth, |
| | route, |
| | interpolated |
| | ) |
| |
|
| | if (paramValue !== undefined) { |
| | interpolated[paramName] = paramValue |
| | } else if (paramType !== 'optional-catchall') { |
| | throw new InvariantError( |
| | `Could not resolve param value for segment: ${paramName}` |
| | ) |
| | } |
| | } |
| |
|
| | |
| | let nextDepth = depth |
| | if ( |
| | appSegment && |
| | appSegment.type !== 'route-group' && |
| | appSegment.type !== 'parallel-route' |
| | ) { |
| | nextDepth++ |
| | } |
| |
|
| | |
| | for (const parallelRoute of Object.values(parallelRoutes)) { |
| | stack.push({ tree: parallelRoute, depth: nextDepth }) |
| | } |
| | } |
| |
|
| | return interpolated |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | export function getDynamicParam( |
| | interpolatedParams: Params, |
| | segmentKey: string, |
| | dynamicParamType: DynamicParamTypesShort, |
| | fallbackRouteParams: OpaqueFallbackRouteParams | null |
| | ): DynamicParam { |
| | let value: string | string[] | undefined = getParamValue( |
| | interpolatedParams, |
| | segmentKey, |
| | fallbackRouteParams |
| | ) |
| |
|
| | |
| | |
| | if (!value || value.length === 0) { |
| | if (dynamicParamType === 'oc') { |
| | return { |
| | param: segmentKey, |
| | value: null, |
| | type: dynamicParamType, |
| | treeSegment: [segmentKey, '', dynamicParamType], |
| | } |
| | } |
| |
|
| | throw new InvariantError( |
| | `Missing value for segment key: "${segmentKey}" with dynamic param type: ${dynamicParamType}` |
| | ) |
| | } |
| |
|
| | return { |
| | param: segmentKey, |
| | |
| | value, |
| | |
| | treeSegment: [ |
| | segmentKey, |
| | Array.isArray(value) ? value.join('/') : value, |
| | dynamicParamType, |
| | ], |
| | type: dynamicParamType, |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | export const PARAMETER_PATTERN = /^([^[]*)\[((?:\[[^\]]*\])|[^\]]+)\](.*)$/ |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | export function parseParameter(param: string) { |
| | const match = param.match(PARAMETER_PATTERN) |
| |
|
| | if (!match) { |
| | return parseMatchedParameter(param) |
| | } |
| |
|
| | return parseMatchedParameter(match[2]) |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | export function parseMatchedParameter(param: string) { |
| | const optional = param.startsWith('[') && param.endsWith(']') |
| | if (optional) { |
| | param = param.slice(1, -1) |
| | } |
| | const repeat = param.startsWith('...') |
| | if (repeat) { |
| | param = param.slice(3) |
| | } |
| | return { key: param, repeat, optional } |
| | } |
| |
|