Update README.md
This commit is contained in:
parent
4b79993d37
commit
13bd8bb851
108
README.md
108
README.md
@ -7,6 +7,7 @@ A server-rendering framework for React, built on [Bun](https://bun.sh). Bunext h
|
|||||||
Bunext is focused on **server-side rendering and processing**. Every page is rendered on the server on every request. The framework deliberately does not implement client-side navigation, SPA routing, or client-side state management — those concerns belong in client-side libraries and are orthogonal to what Bunext is solving.
|
Bunext is focused on **server-side rendering and processing**. Every page is rendered on the server on every request. The framework deliberately does not implement client-side navigation, SPA routing, or client-side state management — those concerns belong in client-side libraries and are orthogonal to what Bunext is solving.
|
||||||
|
|
||||||
The goal is a framework that is:
|
The goal is a framework that is:
|
||||||
|
|
||||||
- Fast — Bun's runtime speed and ESBuild's bundling make the full dev loop snappy
|
- Fast — Bun's runtime speed and ESBuild's bundling make the full dev loop snappy
|
||||||
- Transparent — the entire request pipeline is readable and debugable
|
- Transparent — the entire request pipeline is readable and debugable
|
||||||
- Standard — server functions and API handlers use native Web APIs (`Request`, `Response`, `URL`) with no custom wrappers
|
- Standard — server functions and API handlers use native Web APIs (`Request`, `Response`, `URL`) with no custom wrappers
|
||||||
@ -72,11 +73,10 @@ Install Bunext directly from GitHub:
|
|||||||
bun add github:moduletrace/bunext
|
bun add github:moduletrace/bunext
|
||||||
```
|
```
|
||||||
|
|
||||||
Install required peer dependencies:
|
Install globally:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bun add react react-dom
|
bun add -g github:moduletrace/bunext
|
||||||
bun add -d typescript @types/react @types/react-dom
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@ -102,13 +102,25 @@ export default function HomePage() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Start the development server:
|
3. Add scripts to your `package.json`:
|
||||||
|
|
||||||
```bash
|
```json
|
||||||
bunext dev
|
{
|
||||||
|
"scripts": {
|
||||||
|
"dev": "bunx bunext dev",
|
||||||
|
"build": "bunx bunext build",
|
||||||
|
"start": "bunx bunext start"
|
||||||
|
}
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
4. Open `http://localhost:7000` in your browser.
|
4. Start the development server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
5. Open `http://localhost:7000` in your browser.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -120,18 +132,38 @@ bunext dev
|
|||||||
| `bunext build` | Bundle all pages for production. Outputs artifacts to `public/pages/`. |
|
| `bunext build` | Bundle all pages for production. Outputs artifacts to `public/pages/`. |
|
||||||
| `bunext start` | Start the production server using pre-built artifacts. |
|
| `bunext start` | Start the production server using pre-built artifacts. |
|
||||||
|
|
||||||
|
### Running the CLI
|
||||||
|
|
||||||
|
Bunext exposes a `bunext` binary. How you invoke it depends on how the package is installed:
|
||||||
|
|
||||||
|
**Local install (recommended)** — add scripts to `package.json` and run them with `bun run`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"scripts": {
|
||||||
|
"dev": "bunx bunext dev",
|
||||||
|
"build": "bunx bunext build",
|
||||||
|
"start": "bunx bunext start"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Development
|
bun run dev
|
||||||
|
bun run build
|
||||||
|
bun run start
|
||||||
|
```
|
||||||
|
|
||||||
|
**Global install** — install once and use `bunext` from anywhere:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun add -g github:moduletrace/bunext
|
||||||
bunext dev
|
bunext dev
|
||||||
|
|
||||||
# Production build
|
|
||||||
bunext build
|
bunext build
|
||||||
|
|
||||||
# Production server (must run build first)
|
|
||||||
bunext start
|
bunext start
|
||||||
```
|
```
|
||||||
|
|
||||||
> **Note:** `bunext start` will exit with an error if `public/pages/map.json` does not exist. Always run `bunext build` before `bunext start`.
|
> **Note:** `bunext start` will exit with an error if `public/pages/map.json` does not exist. Always run `bunext build` (or `bun run build`) before `bunext start`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -185,7 +217,7 @@ Dynamic route parameters (e.g. `[slug]`) are available in the `server` function
|
|||||||
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.
|
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 |
|
| Naming pattern | Effect |
|
||||||
| --- | --- |
|
| --------------- | -------------------- |
|
||||||
| `(components)/` | Ignored — not routed |
|
| `(components)/` | Ignored — not routed |
|
||||||
| `--utils--/` | Ignored — not routed |
|
| `--utils--/` | Ignored — not routed |
|
||||||
| `--lib/` | Ignored — not routed |
|
| `--lib/` | Ignored — not routed |
|
||||||
@ -222,7 +254,7 @@ Export a `server` function to run server-side logic before rendering. The return
|
|||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
// src/pages/profile.tsx
|
// src/pages/profile.tsx
|
||||||
import type { BunextPageServerFn } from "bunext/src/types";
|
import type { BunextPageServerFn } from "@moduletrace/bunext/types";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
props?: { username: string; bio: string };
|
props?: { username: string; bio: string };
|
||||||
@ -274,7 +306,7 @@ Every page component automatically receives a `url` prop — a copy of the reque
|
|||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
// src/pages/index.tsx
|
// src/pages/index.tsx
|
||||||
import type { BunextPageModuleServerReturnURLObject } from "bunext/src/types";
|
import type { BunextPageModuleServerReturnURLObject } from "@moduletrace/bunext/types";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
url?: BunextPageModuleServerReturnURLObject;
|
url?: BunextPageModuleServerReturnURLObject;
|
||||||
@ -293,7 +325,7 @@ export default function HomePage({ url }: Props) {
|
|||||||
The `url` prop exposes the following fields from the standard Web `URL` interface:
|
The `url` prop exposes the following fields from the standard Web `URL` interface:
|
||||||
|
|
||||||
| Field | Type | Example |
|
| Field | Type | Example |
|
||||||
| -------------- | ---------------- | -------------------------------- |
|
| -------------- | ----------------- | -------------------------------- |
|
||||||
| `href` | `string` | `"https://example.com/blog?q=1"` |
|
| `href` | `string` | `"https://example.com/blog?q=1"` |
|
||||||
| `origin` | `string` | `"https://example.com"` |
|
| `origin` | `string` | `"https://example.com"` |
|
||||||
| `protocol` | `string` | `"https:"` |
|
| `protocol` | `string` | `"https:"` |
|
||||||
@ -354,7 +386,7 @@ export const server: BunextPageServerFn = async (ctx) => {
|
|||||||
Export a `meta` object to inject SEO and Open Graph tags into the `<head>`:
|
Export a `meta` object to inject SEO and Open Graph tags into the `<head>`:
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
import type { BunextPageModuleMeta } from "bunext/src/types";
|
import type { BunextPageModuleMeta } from "@moduletrace/bunext/types";
|
||||||
|
|
||||||
export const meta: BunextPageModuleMeta = {
|
export const meta: BunextPageModuleMeta = {
|
||||||
title: "My Page Title",
|
title: "My Page Title",
|
||||||
@ -393,7 +425,7 @@ export default function AboutPage() {
|
|||||||
`meta` can also be an async function that receives the request context and server response:
|
`meta` can also be an async function that receives the request context and server response:
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
import type { BunextPageModuleMetaFn } from "bunext/src/types";
|
import type { BunextPageModuleMetaFn } from "@moduletrace/bunext/types";
|
||||||
|
|
||||||
export const meta: BunextPageModuleMetaFn = async ({ ctx, serverRes }) => {
|
export const meta: BunextPageModuleMetaFn = async ({ ctx, serverRes }) => {
|
||||||
return {
|
return {
|
||||||
@ -408,7 +440,7 @@ export const meta: BunextPageModuleMetaFn = async ({ ctx, serverRes }) => {
|
|||||||
Export a `Head` functional component to inject arbitrary HTML into `<head>`. It receives the server response and request context:
|
Export a `Head` functional component to inject arbitrary HTML into `<head>`. It receives the server response and request context:
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
import type { BunextPageHeadFCProps } from "bunext/src/types";
|
import type { BunextPageHeadFCProps } from "@moduletrace/bunext/types";
|
||||||
|
|
||||||
export function Head({ serverRes, ctx }: BunextPageHeadFCProps) {
|
export function Head({ serverRes, ctx }: BunextPageHeadFCProps) {
|
||||||
return (
|
return (
|
||||||
@ -454,7 +486,7 @@ Create files under `src/pages/api/` to define API endpoints. The default export
|
|||||||
|
|
||||||
```ts
|
```ts
|
||||||
// src/pages/api/hello.ts
|
// src/pages/api/hello.ts
|
||||||
import type { BunxRouteParams } from "bunext/src/types";
|
import type { BunxRouteParams } from "@moduletrace/bunext/types";
|
||||||
|
|
||||||
export default async function handler(ctx: BunxRouteParams): Promise<Response> {
|
export default async function handler(ctx: BunxRouteParams): Promise<Response> {
|
||||||
return Response.json({ message: "Hello from the API" });
|
return Response.json({ message: "Hello from the API" });
|
||||||
@ -465,7 +497,7 @@ API routes are matched at `/api/<filename>`. Because the handler returns a plain
|
|||||||
|
|
||||||
```ts
|
```ts
|
||||||
// src/pages/api/users.ts
|
// src/pages/api/users.ts
|
||||||
import type { BunxRouteParams } from "bunext/src/types";
|
import type { BunxRouteParams } from "@moduletrace/bunext/types";
|
||||||
|
|
||||||
export default async function handler(ctx: BunxRouteParams): Promise<Response> {
|
export default async function handler(ctx: BunxRouteParams): Promise<Response> {
|
||||||
if (ctx.req.method !== "GET") {
|
if (ctx.req.method !== "GET") {
|
||||||
@ -488,7 +520,7 @@ Export a `config` object to override the per-route request body limit (default:
|
|||||||
import type {
|
import type {
|
||||||
BunextServerRouteConfig,
|
BunextServerRouteConfig,
|
||||||
BunxRouteParams,
|
BunxRouteParams,
|
||||||
} from "bunext/src/types";
|
} from "@moduletrace/bunext/types";
|
||||||
|
|
||||||
export const config: BunextServerRouteConfig = {
|
export const config: BunextServerRouteConfig = {
|
||||||
maxRequestBodyMB: 50, // allow up to 50 MB
|
maxRequestBodyMB: 50, // allow up to 50 MB
|
||||||
@ -553,7 +585,7 @@ Bunext includes a file-based HTML cache for production. Caching is **disabled in
|
|||||||
Cache files are stored in `public/__bunext/cache/`. Each cached page produces two files:
|
Cache files are stored in `public/__bunext/cache/`. Each cached page produces two files:
|
||||||
|
|
||||||
| File | Contents |
|
| File | Contents |
|
||||||
|---------------------------|----------------------------------------------|
|
| ----------------- | ---------------------------------------------- |
|
||||||
| `<key>.res.html` | The cached HTML response body |
|
| `<key>.res.html` | The cached HTML response body |
|
||||||
| `<key>.meta.json` | Metadata: creation timestamp, expiry, paradigm |
|
| `<key>.meta.json` | Metadata: creation timestamp, expiry, paradigm |
|
||||||
|
|
||||||
@ -565,7 +597,7 @@ Export a `config` object from a page file to opt that page into caching:
|
|||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
// src/pages/products.tsx
|
// src/pages/products.tsx
|
||||||
import type { BunextRouteConfig } from "bunext/src/types";
|
import type { BunextRouteConfig } from "@moduletrace/bunext/types";
|
||||||
|
|
||||||
export const config: BunextRouteConfig = {
|
export const config: BunextRouteConfig = {
|
||||||
cachePage: true,
|
cachePage: true,
|
||||||
@ -582,7 +614,7 @@ export default function ProductsPage() {
|
|||||||
Cache settings can also be returned from the `server` function, which lets you conditionally enable caching based on request data:
|
Cache settings can also be returned from the `server` function, which lets you conditionally enable caching based on request data:
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
import type { BunextPageServerFn } from "bunext/src/types";
|
import type { BunextPageServerFn } from "@moduletrace/bunext/types";
|
||||||
|
|
||||||
export const server: BunextPageServerFn = async (ctx) => {
|
export const server: BunextPageServerFn = async (ctx) => {
|
||||||
const data = await fetchProducts();
|
const data = await fetchProducts();
|
||||||
@ -595,7 +627,13 @@ export const server: BunextPageServerFn = async (ctx) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default function ProductsPage({ props }: any) {
|
export default function ProductsPage({ props }: any) {
|
||||||
return <ul>{props.data.map((p: any) => <li key={p.id}>{p.name}</li>)}</ul>;
|
return (
|
||||||
|
<ul>
|
||||||
|
{props.data.map((p: any) => (
|
||||||
|
<li key={p.id}>{p.name}</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -627,7 +665,7 @@ Create a `bunext.config.ts` file in your project root to configure Bunext:
|
|||||||
|
|
||||||
```ts
|
```ts
|
||||||
// bunext.config.ts
|
// bunext.config.ts
|
||||||
import type { BunextConfig } from "bunext/src/types";
|
import type { BunextConfig } from "@moduletrace/bunext/types";
|
||||||
|
|
||||||
const config: BunextConfig = {
|
const config: BunextConfig = {
|
||||||
port: 3000, // default: 7000
|
port: 3000, // default: 7000
|
||||||
@ -644,14 +682,14 @@ export default config;
|
|||||||
```
|
```
|
||||||
|
|
||||||
| Option | Type | Default | Description |
|
| Option | Type | Default | Description |
|
||||||
| -------------- | --------------------------------------------------------------------------------- | ---------------- | -------------------------------------------------- |
|
| -------------------- | --------------------------------------------------------------------------------- | ---------------- | ------------------------------------------------------------------------------------------------- |
|
||||||
| `port` | `number` | `7000` | HTTP server port |
|
| `port` | `number` | `7000` | HTTP server port |
|
||||||
| `origin` | `string` | — | Canonical origin URL |
|
| `origin` | `string` | — | Canonical origin URL |
|
||||||
| `distDir` | `string` | `.bunext` | Internal artifact directory |
|
| `distDir` | `string` | `.bunext` | Internal artifact directory |
|
||||||
| `assetsPrefix` | `string` | `_bunext/static` | URL prefix for static assets |
|
| `assetsPrefix` | `string` | `_bunext/static` | URL prefix for static assets |
|
||||||
| `globalVars` | `{ [k: string]: any }` | — | Variables injected globally at build time |
|
| `globalVars` | `{ [k: string]: any }` | — | Variables injected globally at build time |
|
||||||
| `development` | `boolean` | — | Overridden to `true` by `bunext dev` automatically |
|
| `development` | `boolean` | — | Overridden to `true` by `bunext dev` automatically |
|
||||||
| `defaultCacheExpiry`| `number` | `3600` | Global page cache expiry in seconds |
|
| `defaultCacheExpiry` | `number` | `3600` | Global page cache expiry in seconds |
|
||||||
| `middleware` | `(params: BunextConfigMiddlewareParams) => Response \| undefined \| Promise<...>` | — | Global middleware — see [Middleware](#middleware) |
|
| `middleware` | `(params: BunextConfigMiddlewareParams) => Response \| undefined \| Promise<...>` | — | Global middleware — see [Middleware](#middleware) |
|
||||||
| `websocket` | `WebSocketHandler<any>` | — | Bun WebSocket handler — see [WebSocket](#websocket) |
|
| `websocket` | `WebSocketHandler<any>` | — | Bun WebSocket handler — see [WebSocket](#websocket) |
|
||||||
| `serverOptions` | `ServeOptions` | — | Extra options passed to `Bun.serve()` (excluding `fetch`) — see [Server Options](#server-options) |
|
| `serverOptions` | `ServeOptions` | — | Extra options passed to `Bun.serve()` (excluding `fetch`) — see [Server Options](#server-options) |
|
||||||
@ -668,7 +706,7 @@ Middleware runs on every request before any routing. Define it in `bunext.config
|
|||||||
import type {
|
import type {
|
||||||
BunextConfig,
|
BunextConfig,
|
||||||
BunextConfigMiddlewareParams,
|
BunextConfigMiddlewareParams,
|
||||||
} from "bunext/src/types";
|
} from "@moduletrace/bunext/types";
|
||||||
|
|
||||||
const config: BunextConfig = {
|
const config: BunextConfig = {
|
||||||
middleware: async ({ req, url }) => {
|
middleware: async ({ req, url }) => {
|
||||||
@ -721,7 +759,7 @@ export const BunextWebsocket: WebSocketHandler<any> = {
|
|||||||
|
|
||||||
```ts
|
```ts
|
||||||
// bunext.config.ts
|
// bunext.config.ts
|
||||||
import type { BunextConfig } from "bunext/src/types";
|
import type { BunextConfig } from "@moduletrace/bunext/types";
|
||||||
import { BunextWebsocket } from "./websocket";
|
import { BunextWebsocket } from "./websocket";
|
||||||
|
|
||||||
const config: BunextConfig = {
|
const config: BunextConfig = {
|
||||||
@ -737,7 +775,7 @@ Pass additional options to the underlying `Bun.serve()` call via `serverOptions`
|
|||||||
|
|
||||||
```ts
|
```ts
|
||||||
// bunext.config.ts
|
// bunext.config.ts
|
||||||
import type { BunextConfig } from "bunext/src/types";
|
import type { BunextConfig } from "@moduletrace/bunext/types";
|
||||||
|
|
||||||
const config: BunextConfig = {
|
const config: BunextConfig = {
|
||||||
serverOptions: {
|
serverOptions: {
|
||||||
@ -764,7 +802,7 @@ For full control over the `Bun.serve()` instance — custom WebSocket upgrade lo
|
|||||||
|
|
||||||
```ts
|
```ts
|
||||||
// server.ts
|
// server.ts
|
||||||
import bunext from "bunext";
|
import bunext from "@moduletrace/bunext";
|
||||||
|
|
||||||
const development = process.env.NODE_ENV === "development";
|
const development = process.env.NODE_ENV === "development";
|
||||||
const port = process.env.PORT || 3700;
|
const port = process.env.PORT || 3700;
|
||||||
@ -787,7 +825,7 @@ bunext.bunextLog.info(`Server running on http://localhost:${server.port} ...`);
|
|||||||
```
|
```
|
||||||
|
|
||||||
| Export | Type | Description |
|
| Export | Type | Description |
|
||||||
|--------|------|-------------|
|
| ------------------------------- | ------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| `bunextInit()` | `() => Promise<void>` | Initializes config, router, and bundler. Must be called before handling requests. |
|
| `bunextInit()` | `() => Promise<void>` | Initializes config, router, and bundler. Must be called before handling requests. |
|
||||||
| `bunextRequestHandler({ req })` | `(params: { req: Request }) => Promise<Response>` | The main Bunext request dispatcher — middleware, routing, SSR, static files. Only `req` is needed; the server instance is managed internally. |
|
| `bunextRequestHandler({ req })` | `(params: { req: Request }) => Promise<Response>` | The main Bunext request dispatcher — middleware, routing, SSR, static files. Only `req` is needed; the server instance is managed internally. |
|
||||||
| `bunextLog` | Logger | Framework logger (`info`, `error`, `success`, `server`, `watch`). |
|
| `bunextLog` | Logger | Framework logger (`info`, `error`, `success`, `server`, `watch`). |
|
||||||
|
|||||||
@ -27,7 +27,7 @@
|
|||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "tsc --watch",
|
"dev": "tsc --watch",
|
||||||
"git:push": "tsc --noEmit && tsc && git add . && git commit -m 'Bugfix. Update generate-web-html function' && git push",
|
"git:push": "tsc --noEmit && tsc && git add . && git commit -m 'Update README.md' && git push",
|
||||||
"compile": "bun build ./src/commands/index.ts --compile --outfile bin/bunext",
|
"compile": "bun build ./src/commands/index.ts --compile --outfile bin/bunext",
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"test": "bun test --max-concurrency=1"
|
"test": "bun test --max-concurrency=1"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user