diff --git a/.claude/skills/funstack-router-knowledge/SKILL.md b/.claude/skills/funstack-router-knowledge/SKILL.md
new file mode 100644
index 0000000..a3f70d4
--- /dev/null
+++ b/.claude/skills/funstack-router-knowledge/SKILL.md
@@ -0,0 +1,23 @@
+---
+name: funstack-router-knowledge
+description: Use this skill when you need information about `@funstack/router` (the React router your app uses). What it is, API references, best practices, etc.
+metadata:
+ internal: true
+---
+
+# FUNSTACK Router Knowledge
+
+**FUNSTACK Router** (`@funstack/router`) is a modern React router built on the [Navigation API](https://developer.mozilla.org/en-US/docs/Web/API/Navigation_API) (not the History API). It uses the [URLPattern API](https://developer.mozilla.org/en-US/docs/Web/API/URLPattern) for path matching.
+
+## Entrypoints
+
+- `@funstack/router` — Main entrypoint. Provides `Router`, `Outlet`, hooks (`useNavigate`, `useRouteParams`, etc.), and route definition utilities (`route()`, `routeState()`).
+- `@funstack/router/server` — Entrypoint for Server context when you are using React Server Components. Provides `route()` and `routeState()` utilities for defining routes in server modules.
+
+## FUNSTACK Router Docs
+
+More detailed documentation (including API references and best practices) can be found at:
+
+```
+node_modules/@funstack/router/dist/docs/index.md
+```
diff --git a/packages/docs/package.json b/packages/docs/package.json
index 9930fe5..6c7724a 100644
--- a/packages/docs/package.json
+++ b/packages/docs/package.json
@@ -11,14 +11,15 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
- "@funstack/router": "0.0.7",
+ "@funstack/router": "^0.0.9",
"@funstack/static": "workspace:*",
"@shikijs/rehype": "^3.22.0",
"@types/node": "catalog:",
"react": "catalog:",
"react-dom": "catalog:",
"rehype-slug": "^6.0.0",
- "shiki": "^3.22.0"
+ "shiki": "^3.22.0",
+ "urlpattern-polyfill": "^10.1.0"
},
"devDependencies": {
"@mdx-js/rollup": "^3.1.1",
diff --git a/packages/docs/src/App.tsx b/packages/docs/src/App.tsx
index 189cb82..9209a96 100644
--- a/packages/docs/src/App.tsx
+++ b/packages/docs/src/App.tsx
@@ -19,7 +19,7 @@ import { Home } from "./pages/Home";
import { NotFound } from "./pages/NotFound";
import { Router } from "./Router";
-const routes: RouteDefinition[] = [
+export const routes: RouteDefinition[] = [
route({
path: import.meta.env.BASE_URL.replace(/\/$/, ""),
component: ,
@@ -132,6 +132,6 @@ const routes: RouteDefinition[] = [
}),
];
-export default function App() {
- return ;
+export default function App({ ssrPath }: { ssrPath: string }) {
+ return ;
}
diff --git a/packages/docs/src/entries.tsx b/packages/docs/src/entries.tsx
new file mode 100644
index 0000000..33a79fb
--- /dev/null
+++ b/packages/docs/src/entries.tsx
@@ -0,0 +1,29 @@
+import "urlpattern-polyfill";
+import type { EntryDefinition } from "@funstack/static/entries";
+import type { RouteDefinition } from "@funstack/router/server";
+import App, { routes } from "./App";
+
+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)}/index.html`;
+}
+
+export default function getEntries(): EntryDefinition[] {
+ return collectPaths(routes).map((pathname) => ({
+ path: pathToEntryPath(pathname),
+ root: () => import("./root"),
+ app: ,
+ }));
+}
diff --git a/packages/docs/vite.config.ts b/packages/docs/vite.config.ts
index cf6958e..0cc5aed 100644
--- a/packages/docs/vite.config.ts
+++ b/packages/docs/vite.config.ts
@@ -12,8 +12,8 @@ export default defineConfig(async () => {
const config: UserConfig = {
plugins: [
funstackStatic({
- root: "./src/root.tsx",
- app: "./src/App.tsx",
+ entries: "./src/entries.tsx",
+ ssr: true,
}),
{
// to make .mdx loading lazy
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 0440e78..d04a42d 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -39,8 +39,8 @@ importers:
packages/docs:
dependencies:
'@funstack/router':
- specifier: 0.0.7
- version: 0.0.7(react@19.2.4)
+ specifier: ^0.0.9
+ version: 0.0.9(react@19.2.4)
'@funstack/static':
specifier: workspace:*
version: link:../static
@@ -62,6 +62,9 @@ importers:
shiki:
specifier: ^3.22.0
version: 3.22.0
+ urlpattern-polyfill:
+ specifier: ^10.1.0
+ version: 10.1.0
devDependencies:
'@mdx-js/rollup':
specifier: ^3.1.1
@@ -617,8 +620,8 @@ packages:
'@noble/hashes':
optional: true
- '@funstack/router@0.0.7':
- resolution: {integrity: sha512-iqdNkCUu7PovXMJjPydFCoZSxz9d37dR/zCLnbfp8TZ9MweXNk+105v1gHHzdpOWEr7PFZWb1vrgsO92hXf8vg==}
+ '@funstack/router@0.0.9':
+ resolution: {integrity: sha512-d+tUZEoBQA0/m06ANLQ3/iATLu4F3ZGY7aDeVGZEwAK07SR9AF1nmG8hBCDDJY4tlwk++xWsViXSZ/gEtnbB5Q==}
hasBin: true
peerDependencies:
react: ^18.0.0 || ^19.0.0
@@ -2399,6 +2402,9 @@ packages:
peerDependencies:
browserslist: '>= 4.21.0'
+ urlpattern-polyfill@10.1.0:
+ resolution: {integrity: sha512-IGjKp/o0NL3Bso1PymYURCJxMPNAf/ILOpendP9f5B6e1rTJgdgiOvgfoT8VxCAdY+Wisb9uhGaJJf3yZ2V9nw==}
+
vfile-message@4.0.3:
resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==}
@@ -2880,7 +2886,7 @@ snapshots:
'@exodus/bytes@1.14.1': {}
- '@funstack/router@0.0.7(react@19.2.4)':
+ '@funstack/router@0.0.9(react@19.2.4)':
dependencies:
'@funstack/skill-installer': 1.0.0
react: 19.2.4
@@ -4954,6 +4960,8 @@ snapshots:
escalade: 3.2.0
picocolors: 1.1.1
+ urlpattern-polyfill@10.1.0: {}
+
vfile-message@4.0.3:
dependencies:
'@types/unist': 3.0.3