UNPKG

expo-router

Version:

Expo Router is a file-based router for React Native and web applications.

163 lines 6.43 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.loadStaticParamsAsync = loadStaticParamsAsync; exports.evalStaticParamsAsync = evalStaticParamsAsync; exports.assertStaticParams = assertStaticParams; async function loadStaticParamsAsync(route) { const expandedChildren = await Promise.all(route.children.map((route) => loadStaticParamsRecursive(route, { parentParams: {} }))); route.children = expandedChildren.flat(); return route; } async function evalStaticParamsAsync(route, props, generateStaticParams) { if (!route.dynamic && generateStaticParams) { throw new Error('Cannot use generateStaticParams in a route without dynamic segments: ' + route.contextKey); } if (generateStaticParams) { const staticParams = await generateStaticParams({ params: props.parentParams || {}, }); assertStaticParamsType(staticParams); // Assert that at least one param from each matches the dynamic route. staticParams.forEach((params) => assertStaticParams(route, params)); return staticParams; } return null; } async function loadStaticParamsRecursive(route, props) { if (!route?.dynamic && !route?.children?.length) { return [route]; } const loaded = await route.loadRoute(); const staticParams = (await evalStaticParamsAsync(route, props, loaded.generateStaticParams)) ?? []; const traverseForNode = async (nextParams) => { const nextChildren = []; for (const child of route.children) { const children = await loadStaticParamsRecursive(child, { ...props, parentParams: nextParams, }); nextChildren.push(...children); } return uniqBy(nextChildren, (i) => i.route); }; if (!staticParams.length) { const nextParams = { ...props.parentParams, }; route.children = await traverseForNode(nextParams); return [route]; } const createParsedRouteName = (input, params) => { let parsedRouteName = input; route.dynamic?.map((query) => { const param = params[query.name]; const formattedParameter = Array.isArray(param) ? param.join('/') : param; if (query.deep) { parsedRouteName = parsedRouteName.replace(`[...${query.name}]`, formattedParameter); } else { parsedRouteName = parsedRouteName.replace(`[${query.name}]`, param); } }); return parsedRouteName; }; const generatedRoutes = await Promise.all(staticParams.map(async (params) => { const nextParams = { ...props.parentParams, ...params, }; const dynamicChildren = await traverseForNode(nextParams); const parsedRoute = createParsedRouteName(route.route, params); const generatedContextKey = createParsedRouteName(route.contextKey, params); return { ...route, // TODO: Add a new field for this contextKey: generatedContextKey, // Convert the dynamic route to a static route. dynamic: null, route: parsedRoute, children: dynamicChildren, }; })); return [route, ...generatedRoutes]; } /** lodash.uniqBy */ function uniqBy(array, key) { const seen = {}; return array.filter((item) => { const k = key(item); if (seen[k]) { return false; } seen[k] = true; return true; }); } function assertStaticParamsType(params) { if (!Array.isArray(params)) { throw new Error(`generateStaticParams() must return an array of params, received ${params}`); } } function formatExpected(expected, received) { const total = { ...received, }; for (const item of expected) { if (total[item] == null) { total[item] = String(total[item]); } else { total[item] = `"${total[item]}"`; } } return [ '{', Object.entries(total) .map(([key, value]) => ` "${key}": ${value}`) .join(',\n'), '}', ].join('\n'); } function assertStaticParams(route, params) { // Type checking if (!route.dynamic) { throw new Error('assertStaticParams() must be called on a dynamic route.'); } const matches = route.dynamic.every((dynamic) => { const value = params[dynamic.name]; return value !== undefined && value !== null; }); if (!matches) { const plural = route.dynamic.length > 1 ? 's' : ''; const expected = route.dynamic.map((dynamic) => dynamic.name); throw new Error(`[${route.contextKey}]: generateStaticParams() must return an array of params that match the dynamic route${plural}. Expected non-nullish values for key${plural}: ${expected .map((v) => `"${v}"`) .join(', ')}.\nReceived:\n${formatExpected(expected, params)}`); } const validateSingleParam = (dynamic, value, allowMultipleSegments) => { if (typeof value !== 'string') { throw new Error(`generateStaticParams() for route "${route.contextKey}" expected param "${dynamic.name}" to be of type string, instead found "${typeof value}" while parsing "${value}".`); } const parts = value.split('/').filter(Boolean); if (parts.length > 1 && !allowMultipleSegments) { throw new Error(`generateStaticParams() for route "${route.contextKey}" expected param "${dynamic.name}" to not contain "/" (multiple segments) while parsing "${value}".`); } if (parts.length === 0) { throw new Error(`generateStaticParams() for route "${route.contextKey}" expected param "${dynamic.name}" not to be empty while parsing "${value}".`); } }; // `[shape]/bar/[...colors]` -> `[shape]`, `[...colors]` for (const dynamic of route.dynamic) { let parameter = params[dynamic.name]; if (dynamic.deep) { if (Array.isArray(parameter)) { parameter = parameter.filter(Boolean).join('/'); } validateSingleParam(dynamic, parameter, true); } else { validateSingleParam(dynamic, parameter); } } } //# sourceMappingURL=loadStaticParamsAsync.js.map