Update documentaion.

This commit is contained in:
Benjamin Toby 2026-03-20 13:22:51 +01:00
parent 39f802d26a
commit 21b2eb8202
5 changed files with 48 additions and 2 deletions

View File

@ -21,6 +21,7 @@ The goal is a framework that is:
- [CLI Commands](#cli-commands) - [CLI Commands](#cli-commands)
- [Project Structure](#project-structure) - [Project Structure](#project-structure)
- [File-System Routing](#file-system-routing) - [File-System Routing](#file-system-routing)
- [Non-Routed Directories](#non-routed-directories)
- [Pages](#pages) - [Pages](#pages)
- [Basic Page](#basic-page) - [Basic Page](#basic-page)
- [Server Function](#server-function) - [Server Function](#server-function)
@ -176,6 +177,27 @@ Bunext uses `Bun.FileSystemRouter` with Next.js-style routing. Pages live in `sr
Dynamic route parameters (e.g. `[slug]`) are available in the `server` function via `ctx.req.url` or from the `query` field in the server response. Dynamic route parameters (e.g. `[slug]`) are available in the `server` function via `ctx.req.url` or from the `query` field in the server response.
### Non-Routed Directories
Directories whose name contains `--` or a parenthesis (`(` or `)`) are completely ignored by the router. Use this to co-locate helper components, utilities, or shared logic directly inside `src/pages/` alongside the routes that use them, without them becoming routes.
| Naming pattern | Effect |
| --- | --- |
| `(components)/` | Ignored — not routed |
| `--utils--/` | Ignored — not routed |
| `--lib/` | Ignored — not routed |
```
src/pages/
├── blog/
│ ├── (components)/ # Not a route — co-location directory
│ │ ├── PostCard.tsx # Used by index.tsx and [slug].tsx
│ │ └── PostList.tsx
│ ├── index.tsx # Route: /blog
│ └── [slug].tsx # Route: /blog/:slug
└── index.tsx # Route: /
```
--- ---
## Pages ## Pages
@ -592,7 +614,7 @@ The cron job checks all cache entries every 30 seconds and deletes any whose age
- **Cold start required.** The cache is populated on the first request; there is no pre-warming step. - **Cold start required.** The cache is populated on the first request; there is no pre-warming step.
- **Immutable within the expiry window.** Once a page is cached, `writeCache` skips all subsequent write attempts for that key until the cron job deletes the expired entry. There is no manual invalidation API. - **Immutable within the expiry window.** Once a page is cached, `writeCache` skips all subsequent write attempts for that key until the cron job deletes the expired entry. There is no manual invalidation API.
- **Cache is not cleared on rebuild.** Deploying a new build does not automatically flush `public/__bunext/cache/`. Stale HTML files referencing old JS bundles can be served until they expire. Clear the cache directory as part of your deploy process if needed. - **Cache is not cleared on rebuild.** Deploying a new build does not automatically flush `public/__bunext/cache/`. Stale HTML files referencing old JS bundles can be served until they expire. Clear the cache directory as part of your deploy process if needed.
- **Key collision with dashes.** Cache keys are derived by replacing every `/` in the URL path with `-`. This means `/foo/bar` and `/foo-bar` produce the same cache filename and will share a cache entry. Avoid enabling `cachePage` on routes where a nested path and a dash-separated path could collide. - **No key collision.** Cache keys are generated via `encodeURIComponent()` on the URL path. `/foo/bar` encodes to `%2Ffoo%2Fbar` and `/foo-bar` to `%2Ffoo-bar` — distinct filenames with no collision risk.
--- ---

View File

@ -76,6 +76,7 @@ This report compares the two on their overlapping surface — server-side render
| Dynamic routes `[param]` | ✅ | ✅ | ✅ | | Dynamic routes `[param]` | ✅ | ✅ | ✅ |
| Catch-all routes `[...slug]` | ✅ | ✅ | ✅ | | Catch-all routes `[...slug]` | ✅ | ✅ | ✅ |
| Optional catch-all `[[...slug]]` | ✅ | ✅ | ✅ | | Optional catch-all `[[...slug]]` | ✅ | ✅ | ✅ |
| Non-routed co-location directories | ✅ | ❌ | ✅ (file-naming model) |
| Route groups `(group)` | ❌ | ❌ | ✅ | | Route groups `(group)` | ❌ | ❌ | ✅ |
| Parallel routes `@slot` | ❌ | ❌ | ✅ | | Parallel routes `@slot` | ❌ | ❌ | ✅ |
| Intercepting routes `(..)` | ❌ | ❌ | ✅ | | Intercepting routes `(..)` | ❌ | ❌ | ✅ |
@ -150,6 +151,20 @@ This report compares the two on their overlapping surface — server-side render
Catch-all routes (`[...slug].tsx`) and optional catch-all routes (`[[...slug]].tsx`) are supported natively by `Bun.FileSystemRouter` and work in Bunext — the test project confirms this with `src/pages/blog/[[...all]]/index.tsx`. Catch-all routes (`[...slug].tsx`) and optional catch-all routes (`[[...slug]].tsx`) are supported natively by `Bun.FileSystemRouter` and work in Bunext — the test project confirms this with `src/pages/blog/[[...all]]/index.tsx`.
Any directory inside `src/pages/` whose name contains `--` or a parenthesis (`(` or `)`) is **completely ignored by the router**. This lets developers co-locate helper components, hooks, and utilities directly alongside the routes that use them without any routing side effects:
```
src/pages/
├── blog/
│ ├── (components)/ ← Not a route — co-location directory
│ │ ├── PostCard.tsx
│ │ └── PostList.tsx
│ ├── index.tsx ← Route: /blog
│ └── [slug].tsx ← Route: /blog/:slug
```
Next.js Pages Router has no equivalent — every `.tsx` file in `pages/` becomes a route, so helper components must live outside the `pages/` tree entirely. Next.js App Router handles this differently via file-naming convention: only files named `page.tsx` or `route.ts` are routes, so any other file can already be co-located freely. Bunext's explicit directory-level marker (`(dir)` or `--dir--`) provides the same benefit on top of a Pages Router-style filesystem convention.
**Gaps:** **Gaps:**
- No route groups — every folder directly contributes to the URL structure. - No route groups — every folder directly contributes to the URL structure.
- Parallel and intercepting routes (App Router features) are absent. These enable patterns like modals that preserve the previous URL (intercepting) or simultaneously rendering multiple independent route segments. - Parallel and intercepting routes (App Router features) are absent. These enable patterns like modals that preserve the previous URL (intercepting) or simultaneously rendering multiple independent route segments.
@ -591,6 +606,12 @@ This model also composes naturally with runtime decisions: the `server` function
Every page component automatically receives a `url` prop (a copy of the request `URL` object) even without a `server` function. In Next.js's Pages Router, `getServerSideProps` must be exported just to access the request URL. The App Router exposes `params` and `searchParams` but not a full `URL` object. Every page component automatically receives a `url` prop (a copy of the request `URL` object) even without a `server` function. In Next.js's Pages Router, `getServerSideProps` must be exported just to access the request URL. The App Router exposes `params` and `searchParams` but not a full `URL` object.
### Co-Location Directories
Bunext's router ignores any directory inside `src/pages/` whose name contains `--` or a parenthesis. This lets helper components, hooks, and utilities live right next to the routes that use them — named `(components)`, `--utils--`, or similar — without any routing side effects.
Next.js Pages Router has no equivalent. Every file in `pages/` becomes a route; helpers must live in a separate root-level directory (`components/`, `lib/`, etc.) rather than alongside the pages that use them. Next.js App Router handles co-location via file-naming convention (only `page.tsx`/`route.ts` are routes), so the problem doesn't arise — but it provides no explicit directory-level exclusion marker.
--- ---
## Where Bunext Lags ## Where Bunext Lags

View File

@ -15,6 +15,8 @@ export default function watcher() {
return; return;
if (!filename.match(/^pages\//)) if (!filename.match(/^pages\//))
return; return;
if (filename.match(/\/(--|\()/))
return;
if (global.RECOMPILING) if (global.RECOMPILING)
return; return;
const fullPath = path.join(SRC_DIR, filename); const fullPath = path.join(SRC_DIR, filename);

View File

@ -2,7 +2,7 @@
"name": "@moduletrace/bunext", "name": "@moduletrace/bunext",
"module": "index.ts", "module": "index.ts",
"type": "module", "type": "module",
"version": "1.0.7", "version": "1.0.5",
"bin": { "bin": {
"bunext": "dist/index.js" "bunext": "dist/index.js"
}, },

View File

@ -18,6 +18,7 @@ export default function watcher() {
if (event !== "rename") return; if (event !== "rename") return;
if (!filename.match(/^pages\//)) return; if (!filename.match(/^pages\//)) return;
if (filename.match(/\/(--|\()/)) return;
if (global.RECOMPILING) return; if (global.RECOMPILING) return;