|
| 1 | +# File-System Routing |
| 2 | + |
| 3 | +FUNSTACK Static does not include a built-in file-system router, but you can implement one in userland using Vite's `import.meta.glob` and a router library like [FUNSTACK Router](https://github.com/uhyo/funstack-router). |
| 4 | + |
| 5 | +## How It Works |
| 6 | + |
| 7 | +The idea is to use `import.meta.glob` to discover page components from a `pages/` directory at compile time, then convert the file paths into route definitions. |
| 8 | + |
| 9 | +```tsx |
| 10 | +import { route, type RouteDefinition } from "@funstack/router/server"; |
| 11 | + |
| 12 | +const pageModules = import.meta.glob<{ default: React.ComponentType }>( |
| 13 | + "./pages/**/*.tsx", |
| 14 | + { eager: true }, |
| 15 | +); |
| 16 | + |
| 17 | +function filePathToUrlPath(filePath: string): string { |
| 18 | + let urlPath = filePath.replace(/^\.\/pages/, "").replace(/\.tsx$/, ""); |
| 19 | + if (urlPath.endsWith("/index")) { |
| 20 | + urlPath = urlPath.slice(0, -"/index".length); |
| 21 | + } |
| 22 | + return urlPath || "/"; |
| 23 | +} |
| 24 | + |
| 25 | +export const routes: RouteDefinition[] = Object.entries(pageModules).map( |
| 26 | + ([filePath, module]) => { |
| 27 | + const Page = module.default; |
| 28 | + return route({ |
| 29 | + path: filePathToUrlPath(filePath), |
| 30 | + component: <Page />, |
| 31 | + }); |
| 32 | + }, |
| 33 | +); |
| 34 | +``` |
| 35 | + |
| 36 | +With this setup, files in the `pages/` directory are automatically mapped to routes: |
| 37 | + |
| 38 | +| File | Route | |
| 39 | +| ---------------------- | -------- | |
| 40 | +| `pages/index.tsx` | `/` | |
| 41 | +| `pages/about.tsx` | `/about` | |
| 42 | +| `pages/blog/index.tsx` | `/blog` | |
| 43 | + |
| 44 | +## Why import.meta.glob? |
| 45 | + |
| 46 | +Using `import.meta.glob` has two key advantages: |
| 47 | + |
| 48 | +- **Automatic discovery** — you don't need to manually register each page. Just add a new `.tsx` file and it becomes a route. |
| 49 | +- **Hot module replacement** — Vite tracks the glob pattern, so adding or removing page files in development triggers an automatic update without a server restart. |
| 50 | + |
| 51 | +## Static Generation |
| 52 | + |
| 53 | +To generate static HTML for each route, derive [entry definitions](/api/entry-definition) from the route list: |
| 54 | + |
| 55 | +```tsx |
| 56 | +import type { EntryDefinition } from "@funstack/static/entries"; |
| 57 | +import type { RouteDefinition } from "@funstack/router/server"; |
| 58 | + |
| 59 | +function collectPaths(routes: RouteDefinition[]): string[] { |
| 60 | + const paths: string[] = []; |
| 61 | + for (const route of routes) { |
| 62 | + if (route.children) { |
| 63 | + paths.push(...collectPaths(route.children)); |
| 64 | + } else if (route.path !== undefined && route.path !== "*") { |
| 65 | + paths.push(route.path); |
| 66 | + } |
| 67 | + } |
| 68 | + return paths; |
| 69 | +} |
| 70 | + |
| 71 | +function pathToEntryPath(path: string): string { |
| 72 | + if (path === "/") return "index.html"; |
| 73 | + return `${path.slice(1)}.html`; |
| 74 | +} |
| 75 | + |
| 76 | +export default function getEntries(): EntryDefinition[] { |
| 77 | + return collectPaths(routes).map((pathname) => ({ |
| 78 | + path: pathToEntryPath(pathname), |
| 79 | + root: () => import("./root"), |
| 80 | + app: <App ssrPath={pathname} />, |
| 81 | + })); |
| 82 | +} |
| 83 | +``` |
| 84 | + |
| 85 | +This produces one HTML file per route at build time. |
| 86 | + |
| 87 | +## Full Example |
| 88 | + |
| 89 | +For a complete working example, see the [`example-fs-routing`](https://github.com/uhyo/funstack-static/tree/master/packages/example-fs-routing) package in the FUNSTACK Static repository. |
| 90 | + |
| 91 | +## See Also |
| 92 | + |
| 93 | +- [Multiple Entrypoints](/advanced/multiple-entrypoints) - Generating multiple HTML pages from a single project |
| 94 | +- [EntryDefinition](/api/entry-definition) - API reference for entry definitions |
| 95 | +- [How It Works](/learn/how-it-works) - Overall FUNSTACK Static architecture |
0 commit comments