weifuwu 0.31.1 → 0.31.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +107 -78
- package/dist/core/router.d.ts +11 -0
- package/dist/index.d.ts +7 -7
- package/dist/index.js +895 -311
- package/dist/middleware/esbuild-dev.d.ts +69 -0
- package/dist/middleware/esbuild-dev.js +335 -0
- package/dist/middleware/tailwind-dev.d.ts +43 -0
- package/dist/middleware/tailwind-dev.js +199 -0
- package/dist/react/client.d.ts +64 -27
- package/dist/react/client.js +130 -248
- package/dist/react/compile.d.ts +17 -0
- package/dist/react/error-boundary.d.ts +18 -19
- package/dist/react/index.d.ts +80 -16
- package/dist/react/index.js +835 -267
- package/dist/react/types.d.ts +134 -34
- package/package.json +16 -6
- package/dist/react/navigation.d.ts +0 -63
- package/dist/react/navigation.js +0 -154
- package/dist/react/render.d.ts +0 -8
- package/dist/react/route-utils.d.ts +0 -31
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@ serve(app, { port: 3000 })
|
|
|
21
21
|
| Export | Description |
|
|
22
22
|
|---|---|
|
|
23
23
|
| `serve(app, opts?)` | Start HTTP server. Returns `Server` with `port`, `hostname`, `ready`, `close()`. |
|
|
24
|
-
| `Router` | Trie-based HTTP router with WebSocket support. |
|
|
24
|
+
| `Router` | Trie-based HTTP router with WebSocket support and `plugin()` method. |
|
|
25
25
|
| `HttpError` | `new HttpError(message, status)`. Throw to return that status code. |
|
|
26
26
|
| `DEFAULT_MAX_BODY` | `10 * 1024 * 1024` (10MB). |
|
|
27
27
|
|
|
@@ -42,6 +42,7 @@ app.ws(path, ...middlewares, handler)
|
|
|
42
42
|
// Middleware & mounting
|
|
43
43
|
app.use(middleware) // global middleware
|
|
44
44
|
app.mount(prefix, router) // sub-router at prefix
|
|
45
|
+
app.plugin(fn) // extension: (app) => { app.get(), app.use(), ... }
|
|
45
46
|
app.onError(handler) // error handler: (error, req, ctx) => Response
|
|
46
47
|
app.routes() // debug: list all registered routes
|
|
47
48
|
|
|
@@ -178,111 +179,139 @@ npm install react react-dom
|
|
|
178
179
|
```
|
|
179
180
|
|
|
180
181
|
```ts
|
|
181
|
-
|
|
182
|
-
import {
|
|
182
|
+
// server.ts — the only file you need
|
|
183
|
+
import { serve, Router } from 'weifuwu'
|
|
184
|
+
import { react } from 'weifuwu/react'
|
|
183
185
|
|
|
184
|
-
|
|
186
|
+
const app = new Router()
|
|
187
|
+
.use(trace())
|
|
188
|
+
.use(logger())
|
|
189
|
+
.plugin(react({
|
|
190
|
+
pages: {
|
|
191
|
+
'/': './pages/Home.tsx',
|
|
192
|
+
'/users': './pages/Users.tsx',
|
|
193
|
+
'/users/:id': './pages/UserDetail.tsx',
|
|
194
|
+
},
|
|
195
|
+
layout: './layouts/Root.tsx',
|
|
196
|
+
notFound: './pages/NotFound.tsx',
|
|
197
|
+
tailwind: { entry: './styles/input.css' },
|
|
198
|
+
}))
|
|
185
199
|
|
|
186
|
-
app.
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
),
|
|
192
|
-
}))
|
|
200
|
+
app.get('/api/hello', () => Response.json({ message: 'hi' }))
|
|
201
|
+
serve(app, { port: 3000 })
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
**One `react()` call handles:** SSR rendering, routing, data loading, Tailwind CSS, client bundle auto-generation, and error pages. No `client.ts`, `routes.ts`, or manual middleware setup needed.
|
|
193
205
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
// Layout nesting via mount
|
|
208
|
-
const admin = new Router()
|
|
209
|
-
admin.use(react({ layout: AdminLayout }))
|
|
210
|
-
admin.get('/dashboard', (_req, ctx) => ctx.render(h(AdminDashboard)))
|
|
211
|
-
app.mount('/admin', admin)
|
|
212
|
-
|
|
213
|
-
// ── Shared components ──────────────────────────────────
|
|
214
|
-
|
|
215
|
-
// Use the same component on server and client
|
|
216
|
-
function Page() {
|
|
217
|
-
const { greeting } = useServerData<{ greeting: string }>()
|
|
218
|
-
return h('h1', null, greeting)
|
|
206
|
+
#### Page components
|
|
207
|
+
|
|
208
|
+
```tsx
|
|
209
|
+
// pages/UserDetail.tsx
|
|
210
|
+
import type { Context } from 'weifuwu'
|
|
211
|
+
import { HttpError } from 'weifuwu'
|
|
212
|
+
import { useServerData } from 'weifuwu/react'
|
|
213
|
+
|
|
214
|
+
export async function loader(ctx: Context) {
|
|
215
|
+
const user = await db.find(ctx.params.id)
|
|
216
|
+
if (!user) throw new HttpError('Not found', 404)
|
|
217
|
+
return { user }
|
|
219
218
|
}
|
|
220
219
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
return
|
|
224
|
-
|
|
225
|
-
|
|
220
|
+
export default function UserDetailPage() {
|
|
221
|
+
const { user } = useServerData<{ user: User }>()
|
|
222
|
+
return (
|
|
223
|
+
<div>
|
|
224
|
+
<title>{`${user.name} — My App`}</title>
|
|
225
|
+
<h1>{user.name}</h1>
|
|
226
|
+
<p>{user.email}</p>
|
|
227
|
+
</div>
|
|
226
228
|
)
|
|
227
229
|
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
- `export async function loader(ctx)` — runs on the server, returns data for `useServerData()`
|
|
233
|
+
- `throw new HttpError('Not found', 404)` — renders the NotFound page with correct status
|
|
234
|
+
- `<title>` auto-hoists to `<head>` via React 19
|
|
235
|
+
- `export default` is auto-detected; named exports work too
|
|
236
|
+
|
|
237
|
+
#### Layout with shared data
|
|
228
238
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
239
|
+
```tsx
|
|
240
|
+
// layouts/Root.tsx
|
|
241
|
+
export async function loader(ctx: Context) {
|
|
242
|
+
return { currentUser: await getCurrentUser(ctx) }
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export function Root({ children }: { children: ReactNode }) {
|
|
246
|
+
const { currentUser } = useServerData()
|
|
247
|
+
return (
|
|
248
|
+
<>
|
|
249
|
+
<nav>
|
|
250
|
+
<a href="/">Home</a>
|
|
251
|
+
<a href="/users">Users</a>
|
|
252
|
+
<span>{currentUser?.name}</span>
|
|
253
|
+
</nav>
|
|
254
|
+
<main>{children}</main>
|
|
255
|
+
</>
|
|
233
256
|
)
|
|
234
257
|
}
|
|
258
|
+
```
|
|
235
259
|
|
|
236
|
-
|
|
260
|
+
Layout `loader` data merges with page data. Page loader overrides same keys.
|
|
237
261
|
|
|
238
|
-
|
|
239
|
-
import { hydrate, createClientRouter, defineRoute } from 'weifuwu/react/client'
|
|
262
|
+
#### Data flow
|
|
240
263
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
264
|
+
```
|
|
265
|
+
layout loader ──┐
|
|
266
|
+
├──→ merge → useServerData() ──→ Layout + Page
|
|
267
|
+
page loader ───┘
|
|
268
|
+
```
|
|
246
269
|
|
|
247
|
-
|
|
248
|
-
{ path: '/', component: HomePage },
|
|
249
|
-
userRoute,
|
|
250
|
-
])
|
|
270
|
+
#### Client-side SPA
|
|
251
271
|
|
|
252
|
-
|
|
272
|
+
Every page is automatically code-split into a separate chunk. On first visit the server renders HTML and the client hydrates. Subsequent navigations intercept `<a>` clicks, `import()` the page chunk, and fetch fresh server data — all automatic, zero config.
|
|
273
|
+
|
|
274
|
+
#### Streaming SSR
|
|
275
|
+
|
|
276
|
+
```tsx
|
|
277
|
+
import { Suspense, use } from 'react'
|
|
278
|
+
|
|
279
|
+
export default function StreamingPage() {
|
|
280
|
+
return (
|
|
281
|
+
<div>
|
|
282
|
+
<h1>Instant shell</h1>
|
|
283
|
+
<Suspense fallback={<Spinner />}>
|
|
284
|
+
<SlowData promise={fetchSlowData()} />
|
|
285
|
+
</Suspense>
|
|
286
|
+
</div>
|
|
287
|
+
)
|
|
288
|
+
}
|
|
253
289
|
```
|
|
254
290
|
|
|
255
|
-
|
|
291
|
+
`<Suspense>` boundaries stream to the browser as data resolves.
|
|
256
292
|
|
|
257
293
|
| Feature | Description |
|
|
258
294
|
|---|---|
|
|
259
|
-
| `
|
|
260
|
-
| `ctx
|
|
261
|
-
| `useServerData<T>()` |
|
|
262
|
-
| `Link` |
|
|
263
|
-
| `
|
|
264
|
-
|
|
|
265
|
-
|
|
|
266
|
-
| `
|
|
267
|
-
| `defineRoute()` | Type-safe route config — captures loader return type. |
|
|
268
|
-
| `createClientRouter()` | Client-side SPA router with loader-based data fetching. |
|
|
269
|
-
| `hydrate(App)` | Hydrate server-rendered HTML on the client. |
|
|
270
|
-
| `head: { title, meta }` | Inject dynamic `<title>` and `<meta>` tags. |
|
|
295
|
+
| `react({ pages, layout, notFound, tailwind })` | One call: SSR + routing + client bundle + error handling |
|
|
296
|
+
| `export async function loader(ctx)` | Server-side data loading, auto-detected by the framework |
|
|
297
|
+
| `useServerData<T>()` | Type-safe access to loader data in any component |
|
|
298
|
+
| `Link` | `<a>` that does SPA navigation on the client |
|
|
299
|
+
| `ErrorBoundary` | Catches render errors on server and client |
|
|
300
|
+
| `<title>`, `<meta>` | Auto-hoisted to `<head>` via React 19 |
|
|
301
|
+
| `<Suspense>` | Streaming SSR, works out of the box |
|
|
302
|
+
| `client: false` | Disable client JS for static SSR only |
|
|
271
303
|
|
|
272
304
|
**Import paths:**
|
|
273
305
|
|
|
274
306
|
```ts
|
|
275
307
|
// Server-side
|
|
276
|
-
import { react, Link,
|
|
277
|
-
|
|
278
|
-
// Client-side (for browser bundles)
|
|
279
|
-
import { hydrate, createClientRouter, defineRoute, Link } from 'weifuwu/react/client'
|
|
308
|
+
import { react, Link, ErrorBoundary, useServerData } from 'weifuwu'
|
|
280
309
|
|
|
281
|
-
//
|
|
282
|
-
import {
|
|
310
|
+
// Client-side (for custom advanced setups)
|
|
311
|
+
import { createBrowserRouter, hydrate, navigate } from 'weifuwu/react/client'
|
|
283
312
|
```
|
|
284
313
|
|
|
285
|
-
See [examples/react-ssr/](examples/react-ssr/) for
|
|
314
|
+
See [examples/react-ssr/](examples/react-ssr/) for the full demo.
|
|
286
315
|
|
|
287
316
|
### Types
|
|
288
317
|
|
package/dist/core/router.d.ts
CHANGED
|
@@ -25,6 +25,17 @@ export declare class Router<T extends Context = Context> {
|
|
|
25
25
|
private get hub();
|
|
26
26
|
wsHub(hub: Hub): this;
|
|
27
27
|
use(mw: Middleware<Context, Context>): Router<T>;
|
|
28
|
+
/**
|
|
29
|
+
* Install a plugin — a function that configures the Router with
|
|
30
|
+
* routes, middleware, and error handlers. Use when `.use()` isn't
|
|
31
|
+
* enough because you need to call `app.get()`, `app.onError()`, etc.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```ts
|
|
35
|
+
* app.plugin(app => createReactApp(app, { pages: {...}, layout: '...', tailwind: {...} }))
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
plugin(fn: (app: this) => void): this;
|
|
28
39
|
mount(path: string, router: Router<Context>): Router<T>;
|
|
29
40
|
onError(handler: ErrorHandler<T>): Router<T>;
|
|
30
41
|
get(path: string, ...rest: [...Middleware[], Handler | Router<Context>]): Router<T>;
|
package/dist/index.d.ts
CHANGED
|
@@ -14,6 +14,10 @@ export { serveStatic } from './middleware/static.ts';
|
|
|
14
14
|
export type { ServeStaticOptions } from './middleware/static.ts';
|
|
15
15
|
export { upload } from './middleware/upload.ts';
|
|
16
16
|
export type { UploadOptions, UploadedFile, UploadModule } from './middleware/upload.ts';
|
|
17
|
+
export { esbuildDev } from './middleware/esbuild-dev.ts';
|
|
18
|
+
export type { EsbuildDevEntry, EsbuildDevOptions } from './middleware/esbuild-dev.ts';
|
|
19
|
+
export { tailwindDev } from './middleware/tailwind-dev.ts';
|
|
20
|
+
export type { TailwindDevEntry, TailwindDevOptions } from './middleware/tailwind-dev.ts';
|
|
17
21
|
export { rateLimit } from './middleware/rate-limit.ts';
|
|
18
22
|
export type { RateLimitOptions } from './middleware/rate-limit.ts';
|
|
19
23
|
export { compress } from './middleware/compress.ts';
|
|
@@ -30,10 +34,6 @@ export { createHub } from './hub.ts';
|
|
|
30
34
|
export type { Hub, HubOptions } from './hub.ts';
|
|
31
35
|
export { queue } from './queue/index.ts';
|
|
32
36
|
export type { QueueOptions, QueueJob, Queue, QueueInjected } from './queue/types.ts';
|
|
33
|
-
export { react } from './react/index.ts';
|
|
34
|
-
export type { ReactOptions, RenderOptions,
|
|
35
|
-
export { useServerData, ServerDataContext } from './react/index.ts';
|
|
36
|
-
export { Link, useParams, useNavigate, useRevalidate, Form, useNavigation } from './react/index.ts';
|
|
37
|
-
export type { LinkProps, FormProps, NavigationState } from './react/index.ts';
|
|
38
|
-
export { ErrorBoundary } from './react/index.ts';
|
|
39
|
-
export type { ErrorBoundaryProps } from './react/index.ts';
|
|
37
|
+
export { react, reactRouter } from './react/index.ts';
|
|
38
|
+
export type { ReactOptions, RenderOptions, ReactRouterOptions, ReactAppOptions } from './react/types.ts';
|
|
39
|
+
export { useServerData, ServerDataContext, Link, ErrorBoundary } from './react/index.ts';
|