Skip to content

Instantly share code, notes, and snippets.

@sabine
Last active October 20, 2022 18:34
Show Gist options
  • Save sabine/21d4138b52a3cc7ef05193ff35a62d2c to your computer and use it in GitHub Desktop.
Save sabine/21d4138b52a3cc7ef05193ff35a62d2c to your computer and use it in GitHub Desktop.
Script to generate typed routes for SvelteKit
import * as fs from 'fs';
import * as path from 'path';
type Route = {
name: string,
parameters: string[],
path: string,
subroutes: Route[],
}
function extract_parameters(filename: string): { parameters: string[], p: string, route_name: string } {
let parameters = [];
let p = filename.replace("+page.svelte", "").replace("%40", "@");
let route_name = filename.replace("%40", "profile").replace("+page.svelte", "");
console.log(["extract_parameters", route_name]);
let match = null;
do {
match = filename.match(/\[(.*?)\]/);
if (match) {
filename = filename.replace(`[${match[1]}]`, "");
route_name = route_name.replace(`[${match[1]}]`, "");
p = p.replace(`[${match[1]}]`, "${" + match[1] + "}");
parameters.push(match[1]);
}
} while (match != null);
if (route_name != "") {
route_name = route_name.split("-").map((x) => x[0].toUpperCase() + x.substring(1)).join("");
}
console.log("extract_parameters", route_name, parameters, p, route_name);
return { parameters, p, route_name };
}
function process_directory(dir: string): Route[] {
let files = fs.readdirSync(dir);
let routes: Route[] = [];
files.forEach(function (file, index) {
let fromPath = path.join(dir, file);
let stat = fs.statSync(fromPath);
if (stat.isFile()) {
console.log("'%s' is a file.", file);
if (file == "+page.svelte") {
let { parameters, p, route_name } = extract_parameters(file);
routes.push({
name: route_name,
parameters: parameters,
path: p,
subroutes: [],
})
}
} else {
if (stat.isDirectory()) {
console.log("'%s' is a directory.", file);
if (!file.startsWith("_") && !file.startsWith("!")) {
let { parameters, p, route_name } = extract_parameters(file);
routes.push({
name: route_name,
parameters: parameters,
path: p,
subroutes: process_directory(path.join(dir, file)),
})
}
}
}
});
return routes;
}
function flatten(arr: any[][]): any[] {
return arr.reduce(function (flat, toFlatten) {
return flat.concat(Array.isArray(toFlatten) ? flatten(toFlatten) : toFlatten);
}, []);
}
function flatten_route(r: Route): Route[] {
if (r.subroutes.length) {
let flattened_routes: Route[] = flatten(r.subroutes.map(flatten_route));
flattened_routes = flattened_routes.map((route) => {
return {
name: `${r.name}${route.name}`,
parameters: [...r.parameters, ...route.parameters],
path: [(r.path != "index" ? [r.path] : []), ...(route.path != "index" ? [route.path] : [])].join("/"),
subroutes: [],
}
});
return flattened_routes;
} else {
return [{
name: r.name,
parameters: r.parameters,
path: (r.path != "index" ? r.path : ""),
subroutes: r.subroutes,
}]
}
}
const BASE_URL = process.env.BASE_URL || "";
function gen_ts_route(r: Route): string {
if (r.parameters.length) {
return `export function ${r.name}(${r.parameters.map((p) => `${p}: string`).join(", ")}): string {
return \`${BASE_URL}/${r.path}\`;
}`;
} else {
return `export const ${r.name == "" ? "Index": r.name}: string = \`${BASE_URL}/${r.path}\`;`;
}
}
export function gen_routes(dir: string) {
let routes = process_directory(dir+"routes/");
console.log(JSON.stringify(routes, null, 2));
let flattened_routes = flatten(routes.map(flatten_route));
console.log(JSON.stringify(flattened_routes, null, 2));
let ts: string = `// AUTOMATICALLY GENERATED, DO NOT MODIFY
export namespace Routes {
// begin routes
${flattened_routes.map(gen_ts_route).join("\n")}
// end routes
}
`;
if (!fs.existsSync(dir+"generated")) fs.mkdirSync(dir+"generated");
fs.writeFileSync(dir+'generated/routes.ts', ts);
}
// AUTOMATICALLY GENERATED, DO NOT MODIFY
export namespace Routes {
// begin routes
export function Profile(username: string): string {
return `/@${username}/`;
}
export function ProfileEdit(username: string): string {
return `/@${username}/edit/`;
}
export function ProfileFavorites(username: string, kind: string): string {
return `/@${username}/favorites/${kind}/`;
}
export function ProfileProjects(username: string): string {
return `/@${username}/projects/`;
}
export function ProfileSettings(username: string): string {
return `/@${username}/settings/`;
}
export const Index: string = `/`;
export const About: string = `/about/`;
export const Add: string = `/add/`;
export const Alerts: string = `/alerts/`;
export const Community: string = `/community/`;
export const Faq: string = `/faq/`;
export const Privacy: string = `/privacy/`;
export function Project(id: string): string {
return `/project/${id}/`;
}
export function ProjectEdit(id: string): string {
return `/project/${id}/edit/`;
}
export const ProjectsNew: string = `/projects/new/`;
export const RefreshSession: string = `/refresh-session`;
export const RequestSignup: string = `/request-signup/`;
export const Signup: string = `/signup/`;
export const SignupSurvey: string = `/signup-survey/`;
export const SiteNotice: string = `/site-notice/`;
export const SitesNew: string = `/sites/new/`;
export const TermsOfUse: string = `/terms-of-use/`;
export const Testing: string = `/testing/`;
// end routes
}
@sabine
Copy link
Author

sabine commented Oct 20, 2022

Since SvelteKit's routing is purely directory-based, you can write a script that generates constants and functions for all your routes. Then, there's no way your internal links can break when you move things around or end up renaming routes. Also good when you're just forgetful, like me, and can't remember how exactly you named your routes. 🤷

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment