rwsdk
Version:
Build fast, server-driven webapps on Cloudflare with SSR, RSC, and realtime
57 lines (56 loc) • 1.96 kB
JavaScript
/**
* Creates a type-safe link generation function from route patterns.
*
* @example
* // Define your routes
* const link = defineLinks([
* "/",
* "/about",
* "/users/:id",
* "/files/*",
* ] as const)
*
* // Generate links with type checking
* link("/") // "/"
* link("/about") // "/about"
* link("/users/:id", { id: "123" }) // "/users/123"
* link("/files/*", { $0: "docs/guide.pdf" }) // "/files/docs/guide.pdf"
*/
export function defineLinks(routes) {
// Validate routes at runtime
routes.forEach((route) => {
if (typeof route !== "string") {
throw new Error(`Invalid route: ${route}. Routes must be strings.`);
}
});
return (path, params) => {
if (!routes.includes(path)) {
throw new Error(`Invalid route: ${path}`);
}
if (!params)
return path;
let result = path;
// Replace named parameters
for (const [key, value] of Object.entries(params)) {
if (key.startsWith("$")) {
// Replace each star with its corresponding $ parameter
const starIndex = parseInt(key.slice(1));
const stars = result.match(/\*/g) || [];
if (starIndex >= stars.length) {
throw new Error(`Parameter ${key} has no corresponding * in route`);
}
// Replace the nth star with the value
let count = 0;
result = result.replace(/\*/g, (match) => count++ === starIndex ? value : match);
}
else {
// Handle named parameters
if (typeof value !== "string") {
throw new Error(`Parameter ${key} must be a string`);
}
result = result.replace(`:${key}`, value);
}
}
return result;
};
}