Last active
October 20, 2022 18:34
-
-
Save sabine/21d4138b52a3cc7ef05193ff35a62d2c to your computer and use it in GitHub Desktop.
Script to generate typed routes for SvelteKit
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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. 🤷