expo-router
Version:
Expo Router is a file-based router for React Native and web applications.
163 lines • 6.43 kB
JavaScript
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
;