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

92 lines (70 loc) 2.85 kB
import {difference, intersection, isPlainObject, pick} from 'lodash' import { type InternalSearchParam, type MatchError, type MatchOk, type MatchResult, type RouterNode, type RouterState, } from './types' import {arrayify} from './utils/arrayify' function isRecord(value: unknown): value is Record<string, unknown> { return isPlainObject(value) } function createMatchError( node: RouterNode, missingKeys: string[], unmappableStateKeys: string[], ): MatchError { return {type: 'error', node, missingKeys, unmappableStateKeys} } function createMatchOk( node: RouterNode, matchedState: Record<string, string>, searchParams: InternalSearchParam[], child?: MatchOk | undefined, ): MatchOk { return {type: 'ok', node, matchedState, searchParams, child} } /** @internal */ export function _findMatchingRoutes(node: RouterNode, _state?: RouterState): MatchResult { if (!_state) { return createMatchOk(node, {}, []) } const scopedState = node.scope ? (_state[node.scope] as RouterState) : _state const {_searchParams: searchParams = [], ...state} = scopedState || {} const requiredParams = node.route.segments .filter((seg) => seg.type === 'param') .map((seg) => seg.name) const stateKeys = isRecord(state) ? Object.keys(state) : [] // These are params found in both the state and the route definition const consumedParams = intersection(stateKeys, requiredParams) // these are params found in the route definition but not in the state, can't map them to a route const missingParams = difference(requiredParams, consumedParams) // these are params found in the state but not in the route definition const remainingParams = difference(stateKeys, consumedParams) if (missingParams.length > 0) { return createMatchError(node, missingParams, []) } const scopedParams = searchParams.map(([key, value]): InternalSearchParam => [[key], value]) const consumedState = pick(state, consumedParams) as Record<string, string> if (remainingParams.length === 0) { return createMatchOk(node, consumedState, scopedParams) } const children = arrayify( (typeof node.children === 'function' ? node.children(isRecord(state) ? state : {}) : node.children) || [], ) if (remainingParams.length > 0 && children.length === 0) { // our state includes extra keys that's not consumed by child routes return createMatchError(node, [], remainingParams) } const remainingState = pick(state, remainingParams) const childResult = children.map((childNode) => _findMatchingRoutes(childNode, remainingState)) // Look for a matching route const found = childResult.find((res): res is MatchOk => res.type === 'ok') return found ? createMatchOk(node, consumedState, scopedParams, found) : createMatchError(node, [], remainingParams) }