UNPKG

type-route

Version:

The flexible, type safe routing library.

162 lines (140 loc) 4.12 kB
import { RouterLocation, UmbrellaParamDef, UmbrellaParamDefCollection, QueryStringSerializer, PathDef, } from "./types"; import { assert } from "./assert"; import { TypeRouteError } from "./TypeRouteError"; import { typeOf } from "./typeOf"; type ParamWithContextCollection = Record< string, { valueSerializerId?: string; array: boolean; value: string | null } >; export function createLocation({ paramCollection, paramDefCollection, arraySeparator, queryStringSerializer, pathDefs, baseUrl, }: { paramCollection: Record<string, unknown>; paramDefCollection: UmbrellaParamDefCollection; arraySeparator: string; queryStringSerializer: QueryStringSerializer; pathDefs: PathDef[]; baseUrl: string; }): RouterLocation { const params = { path: {} as ParamWithContextCollection, query: {} as ParamWithContextCollection, state: {} as ParamWithContextCollection, }; for (const paramName in paramDefCollection) { const paramValue = paramCollection[paramName]; if (paramValue === undefined) { continue; } const paramDef = paramDefCollection[paramName]; const urlEncodeDefault = paramDef["~internal"].kind !== "state" && !paramDef["~internal"].trailing; const urlEncode = paramDef["~internal"].valueSerializer.urlEncode ?? urlEncodeDefault; const getValue = (paramValue: unknown) => { let value: string | null; if (paramDef["~internal"].array) { if (!Array.isArray(paramValue)) { if (__DEV__) { throw TypeRouteError.Expected_type_does_not_match_actual_type.create( { context: "routes[routeName](...)", actualType: typeOf(paramValue), expectedType: "array", value: paramValue, valueName: paramName, } ); } } value = (paramValue as unknown[]).length === 0 ? null : (paramValue as unknown[]) .map((part) => stringify(paramDef, part, urlEncode)) .join(arraySeparator); } else { value = stringify(paramDef, paramValue, urlEncode); } return value; }; const value = getValue(paramValue); if ( paramDef["~internal"].kind === "query" && paramDef["~internal"].default !== undefined && getValue(paramDef["~internal"].default) === value ) { continue; } params[paramDef["~internal"].kind][paramName] = { valueSerializerId: paramDef["~internal"].valueSerializer.id, array: paramDef["~internal"].array, value, }; } const path = "/" + pathDefs[0] .filter(({ namedParamDef }) => { return !( namedParamDef?.["~internal"].optional && params.path[namedParamDef.paramName] === undefined ); }) .map(({ namedParamDef, leading, trailing }) => { const rawParam = namedParamDef ? params.path[namedParamDef.paramName].value : ""; return leading + rawParam + trailing; }) .join("/"); const hasQueryParams = Object.keys(params.query).length > 0; const query = hasQueryParams ? queryStringSerializer.stringify(params.query) : undefined; if (__DEV__) { if (hasQueryParams) { assert("query", [assert.type("string", "query", query)]); } } const state = Object.keys(params.state).length === 0 ? undefined : Object.keys(params.state).reduce( (state, key) => ({ ...state, [key]: params.state[key].value, }), {} ); return { fullPath: (baseUrl === "/" ? "" : baseUrl) + path, path, query, state, }; } function stringify( paramDef: UmbrellaParamDef, value: unknown, urlEncode: boolean ) { const result = paramDef["~internal"].valueSerializer.stringify(value); if (__DEV__) { assert("[ValueSerializer].stringify", [ assert.type("string", "result", result), ]); } return urlEncode ? encodeURIComponent(result) : result; }