UNPKG

sanity

Version:

Sanity is a real-time content infrastructure with a scalable, hosted backend featuring a Graph Oriented Query Language (GROQ), asset pipelines and fast edge caches

109 lines (88 loc) 3.25 kB
import {_findMatchingRoutes} from './_findMatchingRoutes' import {encodeURIComponentExcept} from './encodeURIComponentExcept' import {type InternalSearchParam, type MatchOk, type RouterNode, type RouterState} from './types' import {debug} from './utils/debug' /** @internal */ export function _resolvePathFromState(node: RouterNode, _state: RouterState): string { debug('Resolving path from state %o', _state) const match = _findMatchingRoutes(node, _state) if (match.type === 'error') { const unmappable = match.unmappableStateKeys if (unmappable.length > 0) { throw new Error( `Unable to find matching route for state. Could not map the following state key${ unmappable.length == 1 ? '' : 's' } to a valid url: ${unmappable.map(quote).join(', ')}`, ) } const missingKeys = match.missingKeys throw new Error( `Unable to find matching route for state. State object is missing the following key${ missingKeys.length == 1 ? '' : 's' } defined in route: ${missingKeys.map(quote).join(', ')}`, ) } const {path, searchParams} = pathFromMatchResult(match) const search = searchParams.length > 0 ? encodeParams(searchParams) : '' return `/${path.join('/')}${search ? `?${search}` : ''}` } function bracketify(value: string): string { return `[${value}]` } function encodeParams(params: InternalSearchParam[]): string { return params .flatMap(([key, value]) => { if (value === undefined) { return [] } return [encodeSearchParamKey(serializeScopedPath(key)), encodeSearchParamValue(value)].join( '=', ) }) .join('&') } function serializeScopedPath(scopedPath: string[]): string { const [head, ...tail] = scopedPath return tail.length > 0 ? [head, ...tail.map(bracketify)].join('') : head } function encodeSearchParamValue(value: string): string { return encodeURIComponentExcept(value, '/') } function encodeSearchParamKey(value: string): string { return encodeURIComponentExcept(value, '[]') } function pathFromMatchResult(match: MatchOk): { path: string[] searchParams: InternalSearchParam[] } { const matchedState = match.matchedState const base = match.node.route.segments.map((segment) => { if (segment.type === 'dir') { return segment.name } const transform = match.node.transform && match.node.transform[segment.name] return transform ? transform.toPath(matchedState[segment.name] as any) : matchedState[segment.name] }) const childMatch = match.child ? pathFromMatchResult(match.child) : undefined const searchParams = childMatch?.searchParams ? [...match.searchParams, ...childMatch.searchParams] : match.searchParams return { searchParams: addNodeScope(match.node, searchParams), path: [...(base || []), ...(childMatch?.path || [])], } } function addNodeScope( node: RouterNode, searchParams: InternalSearchParam[], ): InternalSearchParam[] { const scope = node.scope return scope && !node.__unsafe_disableScopedSearchParams ? searchParams.map(([namespaces, value]) => [[scope, ...namespaces], value]) : searchParams } function quote(value: string): string { return `"${value}"` }