feat: built-in file-system routing (experimental)#123
Merged
Conversation
Add a built-in file-system router so users no longer have to wire up routing in userland. Pages discovered under a configurable directory are mapped to routes and rendered with FUNSTACK Router, generating one static HTML file per route. - New `fsRoutes` plugin option (mutually exclusive with `root`+`app` and `entries`), taking `dir`, `root`, and an optional `adapter`. - Convention is pluggable via an `FsRoutesAdapter` (adapter pattern). - Built-in Next.js-like adapter (`nextRoutes`) supporting `page`/`layout` files, dynamic `[param]` and catch-all `[...param]` segments, and `(group)` route groups. Dynamic routes are statically generated via a `generateStaticParams` export. - Public entry `@funstack/static/fs-routes` exporting `nextRoutes` and the adapter types; `@funstack/router` is an optional peer dependency. - Unit tests for the adapter and static-path collection; e2e fixture and specs; example-fs-routing converted to the built-in feature; docs updated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012L8PCU7xhNXfuepS7eKzro
Address review feedback: - Drop the dedicated `fsRoutes` plugin option. File-system routing is now set up entirely through the existing `entries` option: the user writes an entries module that globs pages with `import.meta.glob` and passes them to `createFsRoutesEntries` from `@funstack/static/fs-routes`, along with the `root` component and an optional `adapter`. This keeps the plugin surface to two modes and lets the glob/Root/adapter be plain values. - `createFsRoutesEntries` now auto-strips the common glob prefix (`modulesToRouteFiles`), so no directory needs to be configured twice. - Mark file-system routing as experimental (not yet covered by semantic versioning) in the public API JSDoc and docs. - Update the example, e2e fixture, and docs to the entries-based usage. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012L8PCU7xhNXfuepS7eKzro
Bring back the dedicated `fsRoutes` plugin option as the user-facing way to
enable file-system routing, so users don't have to write `import.meta.glob`
themselves. The plugin synthesizes the entries module (emitting the glob in a
virtual module that Vite transforms) and delegates to `createFsRoutesEntries`
from `@funstack/static/fs-routes`, which remains exported as the underlying
primitive and escape hatch.
- `fsRoutes: { dir?, root, adapter? }` config; mutually exclusive with
`root`+`app` and `entries`.
- Generated module relies on the runtime's automatic common-prefix stripping,
so no separate base needs to be threaded through.
- Marked experimental (not yet covered by semantic versioning).
- Example, e2e fixture, and docs switched back to the `fsRoutes` option.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012L8PCU7xhNXfuepS7eKzro
The dev server (vite dev) could not render file-system routes with the default `ssr: false`: pages are server components rendered through FUNSTACK Router (a client component), and @vitejs/plugin-rsc serializes that server-component route data as eval'd dev-JSX that fails to load in the browser. Production builds are unaffected (the RSC payload is pre-rendered), and enabling `ssr: true` renders pages on the server in dev as well. Set `ssr: true` in the example and e2e fixture, and document that SSR is required for the dev server (and recommended for SEO / initial load). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012L8PCU7xhNXfuepS7eKzro
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012L8PCU7xhNXfuepS7eKzro
This file contains hidden or 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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds built-in file-system routing to
@funstack/static, so users no longer have to wire up routing in userland (as in the previousexample-fs-routing). Pages discovered under a configurable directory are mapped to routes and rendered with FUNSTACK Router, generating one static HTML file per route.This feature is marked experimental and is not yet covered by semantic versioning.
Usage
The new
fsRoutesmode is mutually exclusive withroot+app(single-entry) andentries(multiple entries). The plugin synthesizes the entries module (emittingimport.meta.globin a virtual module that Vite transforms), so the user writes no glob themselves.Design
@funstack/routeris an optional peer dependency, required only when using this feature.FsRoutesAdapter) — the directory/file-name convention is decoupled from the framework's tree→routes/entries logic.nextRoutes) supportingpage/layoutfiles, dynamic[param]and catch-all[...param]segments, and(group)route groups.generateStaticParamsexport (Next.js-like); pages receive resolvedparamsas a prop.createFsRoutesEntriesandnextRoutesare exported from@funstack/static/fs-routesas an escape hatch for fully custom routing.Known limitation
Because static hosting serves one pre-rendered RSC payload per page, soft client-side navigation between different values of the same dynamic route reflects the params of the initially-loaded page. Loading a dynamic URL directly (or via the SPA fallback) always renders correct params, and static routes + layouts navigate fully on the client. This is documented in the guide.
Testing
51 passed).fixture-fs-routing) + build/dev specs wired into Playwright.example-fs-routingconverted to the built-in feature.fsRoutesoption in the API reference.build,typecheck,lint,format:checkall pass.🤖 Generated with Claude Code
https://claude.ai/code/session_012L8PCU7xhNXfuepS7eKzro
Generated by Claude Code