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.
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.
import { route, type RouteDefinition } from "@funstack/router/server";
const pageModules = import.meta.glob<{ default: React.ComponentType }>(
"./pages/**/*.tsx",
{ eager: true },
);
function filePathToUrlPath(filePath: string): string {
let urlPath = filePath.replace(/^\.\/pages/, "").replace(/\.tsx$/, "");
if (urlPath.endsWith("/index")) {
urlPath = urlPath.slice(0, -"/index".length);
}
return urlPath || "/";
}
export const routes: RouteDefinition[] = Object.entries(pageModules).map(
([filePath, module]) => {
const Page = module.default;
return route({
path: filePathToUrlPath(filePath),
component: <Page />,
});
},
);
With this setup, files in the pages/ directory are automatically mapped to routes:
| File | Route |
| ---------------------- | -------- |
| pages/index.tsx | / |
| pages/about.tsx | /about |
| pages/blog/index.tsx | /blog |
Using import.meta.glob has two key advantages:
.tsx file and it becomes a route.To generate static HTML for each route, derive entry definitions from the route list:
import type { EntryDefinition } from "@funstack/static/entries";
import type { RouteDefinition } from "@funstack/router/server";
function collectPaths(routes: RouteDefinition[]): string[] {
const paths: string[] = [];
for (const route of routes) {
if (route.children) {
paths.push(...collectPaths(route.children));
} else if (route.path !== undefined && route.path !== "*") {
paths.push(route.path);
}
}
return paths;
}
function pathToEntryPath(path: string): string {
if (path === "/") return "index.html";
return `${path.slice(1)}.html`;
}
export default function getEntries(): EntryDefinition[] {
return collectPaths(routes).map((pathname) => ({
path: pathToEntryPath(pathname),
root: () => import("./root"),
app: <App ssrPath={pathname} />,
}));
}
This produces one HTML file per route at build time.
For a complete working example, see the example-fs-routing package in the FUNSTACK Static repository.