bini-router 1.0.30 → 1.0.32
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 +26 -15
- package/dist/index.cjs +59 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +59 -23
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -246,6 +246,8 @@ export default function Docs() {
|
|
|
246
246
|
|
|
247
247
|
Layouts wrap all pages in their directory and subdirectories. bini-router walks up the directory tree from each page to collect the full layout chain, stopping at the `appDir` root.
|
|
248
248
|
|
|
249
|
+
All layouts — including the root layout — are rendered as React Router `<Route element>` wrappers using `<Outlet />`. The root layout receives child routes via `<Outlet />` exactly like nested layouts do.
|
|
250
|
+
|
|
249
251
|
```tsx
|
|
250
252
|
// src/app/layout.tsx — root layout
|
|
251
253
|
export const metadata = {
|
|
@@ -253,13 +255,13 @@ export const metadata = {
|
|
|
253
255
|
description: 'Built with bini-router',
|
|
254
256
|
}
|
|
255
257
|
|
|
256
|
-
export default function RootLayout(
|
|
257
|
-
return
|
|
258
|
+
export default function RootLayout() {
|
|
259
|
+
return <Outlet />
|
|
258
260
|
}
|
|
259
261
|
```
|
|
260
262
|
|
|
261
263
|
```tsx
|
|
262
|
-
// src/app/dashboard/layout.tsx — nested layout
|
|
264
|
+
// src/app/dashboard/layout.tsx — nested layout
|
|
263
265
|
export const metadata = {
|
|
264
266
|
title: 'Dashboard',
|
|
265
267
|
}
|
|
@@ -274,8 +276,6 @@ export default function DashboardLayout() {
|
|
|
274
276
|
}
|
|
275
277
|
```
|
|
276
278
|
|
|
277
|
-
> **Root layout** uses `{children}` — it wraps `<BrowserRouter>` from outside.
|
|
278
|
-
> **Nested layouts** use `<Outlet />` — they are React Router route wrappers.
|
|
279
279
|
> Layouts that contain an `<html>` tag are automatically excluded from the chain (treated as HTML shell files, not route layouts).
|
|
280
280
|
> Layouts without a default export are also excluded from the chain.
|
|
281
281
|
> Circular layout dependencies are detected and throw a `CircularLayoutError`.
|
|
@@ -376,7 +376,7 @@ API handlers are loaded on-demand and cached by `mtime` — touching a file in d
|
|
|
376
376
|
import { Hono } from 'hono'
|
|
377
377
|
import nodemailer from 'nodemailer'
|
|
378
378
|
|
|
379
|
-
const app = new Hono()
|
|
379
|
+
const app = new Hono()
|
|
380
380
|
|
|
381
381
|
const transporter = nodemailer.createTransport({
|
|
382
382
|
host: 'smtp-relay.brevo.com',
|
|
@@ -396,7 +396,7 @@ app.post('/email', async (c) => {
|
|
|
396
396
|
export default app
|
|
397
397
|
```
|
|
398
398
|
|
|
399
|
-
> Hono apps are detected by checking for `from 'hono'` in the file source.
|
|
399
|
+
> Hono apps are detected by checking for `from 'hono'` in the file source. Do **not** call `.basePath('/api')` — bini-router already mounts the app under `/api` in the production entry. Adding it yourself will result in double-prefixed routes (`/api/api/...`).
|
|
400
400
|
|
|
401
401
|
### Plain function handlers
|
|
402
402
|
|
|
@@ -415,7 +415,7 @@ Route params are passed via the `x-bini-params` request header as a JSON string
|
|
|
415
415
|
// src/app/api/posts/[id].ts
|
|
416
416
|
import { Hono } from 'hono'
|
|
417
417
|
|
|
418
|
-
const app = new Hono()
|
|
418
|
+
const app = new Hono()
|
|
419
419
|
app.get('/posts/:id', (c) => c.json({ id: c.req.param('id') }))
|
|
420
420
|
export default app
|
|
421
421
|
```
|
|
@@ -450,6 +450,8 @@ biniroute({ platform: 'netlify' })
|
|
|
450
450
|
|
|
451
451
|
Generates `netlify/edge-functions/api.ts` using Deno CDN URL imports (`hono@v4.3.11`) — no npm deps needed in the edge function.
|
|
452
452
|
|
|
453
|
+
> ⚠️ **Netlify Edge Functions run on the Deno runtime, not Node.js.** Node-specific packages like `nodemailer`, `fs`, `path`, or anything that depends on Node built-ins will not work. Use Deno-compatible or Web API alternatives instead (e.g. `fetch` for HTTP, Deno CDN imports for utilities).
|
|
454
|
+
|
|
453
455
|
Add `netlify.toml`:
|
|
454
456
|
|
|
455
457
|
```toml
|
|
@@ -518,9 +520,7 @@ directory = "./dist"
|
|
|
518
520
|
binding = "ASSETS"
|
|
519
521
|
```
|
|
520
522
|
|
|
521
|
-
|
|
522
|
-
vite build && npx wrangler deploy
|
|
523
|
-
```
|
|
523
|
+
Run `vite build` — the worker file is generated automatically and picked up by the Cloudflare dashboard on deploy.
|
|
524
524
|
|
|
525
525
|
---
|
|
526
526
|
|
|
@@ -542,9 +542,20 @@ biniroute({ platform: 'deno' })
|
|
|
542
542
|
|
|
543
543
|
Generates `server/index.ts` (or `server/index.js`) using Deno CDN imports (`hono@v4.3.11`) and `Deno.serve`. Port defaults to `3000` or reads from the `PORT` environment variable.
|
|
544
544
|
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
545
|
+
> ⚠️ **Deno Deploy does not run Node.js.** Node-specific packages like `nodemailer`, `fs`, `path`, or anything that depends on Node built-ins will not work. Use Deno-compatible or Web API alternatives instead (e.g. `fetch` for HTTP, Deno CDN imports for utilities).
|
|
546
|
+
|
|
547
|
+
> ⚠️ **Deno Deploy reads `server/` before the build step runs.** You must commit the generated file:
|
|
548
|
+
>
|
|
549
|
+
> ```bash
|
|
550
|
+
> git add server/index.ts
|
|
551
|
+
> git commit -m "chore: update deno server entry"
|
|
552
|
+
> git push
|
|
553
|
+
> ```
|
|
554
|
+
|
|
555
|
+
In Deno Console, set:
|
|
556
|
+
- **Entrypoint**: `server/index.ts`
|
|
557
|
+
- **Build Command**: `vite build`
|
|
558
|
+
- **Runtime**: Dynamic App
|
|
548
559
|
|
|
549
560
|
---
|
|
550
561
|
|
|
@@ -564,7 +575,7 @@ With `basePath: '/app'`:
|
|
|
564
575
|
- `src/app/api/users.ts` → `/app/api/users`
|
|
565
576
|
- `BrowserRouter basename` is set to `"/app"` at build time
|
|
566
577
|
|
|
567
|
-
> Without `basePath` set, `basename`
|
|
578
|
+
> Without `basePath` set, `basename` falls back to `import.meta.env.BASE_URL` and then `"/"`.
|
|
568
579
|
|
|
569
580
|
---
|
|
570
581
|
|
package/dist/index.cjs
CHANGED
|
@@ -1176,7 +1176,7 @@ var ADAPTERS = {
|
|
|
1176
1176
|
importLine: `import { Hono } from 'https://deno.land/x/hono@v4.3.11/mod.ts';
|
|
1177
1177
|
import { handle } from 'https://deno.land/x/hono@v4.3.11/adapter/netlify/index.ts';`,
|
|
1178
1178
|
exportLine: `export default handle(app);`,
|
|
1179
|
-
outFile: (cwd) => path__default.default.join(cwd, "netlify", "edge-functions", "api.ts"),
|
|
1179
|
+
outFile: (cwd, ts) => path__default.default.join(cwd, "netlify", "edge-functions", ts ? "api.ts" : "api.js"),
|
|
1180
1180
|
stripsApiPrefix: false,
|
|
1181
1181
|
usesDenoRuntime: true
|
|
1182
1182
|
},
|
|
@@ -1349,21 +1349,37 @@ function buildProductionEntry(srcApiDir, platform, enableCors, basePath = "") {
|
|
|
1349
1349
|
lines.push(...[
|
|
1350
1350
|
`// Serve static files from dist (must come AFTER API routes)`,
|
|
1351
1351
|
`app.get('/*', async (c) => {`,
|
|
1352
|
+
` // Skip API routes - let them be handled first`,
|
|
1353
|
+
` if (c.req.path.startsWith('/api/')) {`,
|
|
1354
|
+
` return c.text('API route not found', 404);`,
|
|
1355
|
+
` }`,
|
|
1356
|
+
` `,
|
|
1357
|
+
` const filePath = c.req.path === '/' ? '/index.html' : c.req.path;`,
|
|
1358
|
+
` `,
|
|
1352
1359
|
` try {`,
|
|
1353
|
-
` const
|
|
1354
|
-
` const
|
|
1355
|
-
` const
|
|
1356
|
-
`
|
|
1357
|
-
`
|
|
1358
|
-
`
|
|
1359
|
-
`
|
|
1360
|
-
`
|
|
1360
|
+
` const file = await Deno.readFile(\`./dist\${filePath}\`);`,
|
|
1361
|
+
` const ext = filePath.split('.').pop();`,
|
|
1362
|
+
` const contentType = `,
|
|
1363
|
+
` ext === 'html' ? 'text/html' :`,
|
|
1364
|
+
` ext === 'css' ? 'text/css' :`,
|
|
1365
|
+
` ext === 'js' ? 'application/javascript' :`,
|
|
1366
|
+
` ext === 'json' ? 'application/json' :`,
|
|
1367
|
+
` ext === 'png' ? 'image/png' :`,
|
|
1368
|
+
` ext === 'jpg' || ext === 'jpeg' ? 'image/jpeg' :`,
|
|
1369
|
+
` 'text/plain';`,
|
|
1370
|
+
` `,
|
|
1371
|
+
` return new Response(file, {`,
|
|
1372
|
+
` headers: { 'Content-Type': contentType }`,
|
|
1373
|
+
` });`,
|
|
1361
1374
|
` } catch {`,
|
|
1375
|
+
` // SPA fallback`,
|
|
1362
1376
|
` try {`,
|
|
1363
1377
|
` const indexHtml = await Deno.readFile('./dist/index.html');`,
|
|
1364
|
-
` return new Response(indexHtml, {
|
|
1378
|
+
` return new Response(indexHtml, {`,
|
|
1379
|
+
` headers: { 'Content-Type': 'text/html' }`,
|
|
1380
|
+
` });`,
|
|
1365
1381
|
` } catch {`,
|
|
1366
|
-
` return
|
|
1382
|
+
` return c.text('File not found', 404);`,
|
|
1367
1383
|
` }`,
|
|
1368
1384
|
` }`,
|
|
1369
1385
|
`});`,
|
|
@@ -1394,36 +1410,56 @@ function buildProductionEntry(srcApiDir, platform, enableCors, basePath = "") {
|
|
|
1394
1410
|
fs__default.default.writeFileSync(outFile, lines.join("\n") + "\n", "utf8");
|
|
1395
1411
|
console.log(`\x1B[90m${(/* @__PURE__ */ new Date()).toLocaleTimeString()}\x1B[0m \x1B[36m[vite]\x1B[0m \x1B[32m\u2713 Generated\x1B[0m \x1B[90m${toPosixPath(path__default.default.relative(cwd, outFile))}\x1B[0m`);
|
|
1396
1412
|
if (platform === "vercel") {
|
|
1413
|
+
const serverFile = path__default.default.relative(cwd, outFile);
|
|
1397
1414
|
console.log(`
|
|
1398
1415
|
\x1B[90m${(/* @__PURE__ */ new Date()).toLocaleTimeString()}\x1B[0m \x1B[33m[vite]\x1B[0m \x1B[33m\u26A0\uFE0F Vercel platform detected.\x1B[0m`);
|
|
1399
1416
|
console.log(` Vercel reads your api/ directory BEFORE the build step runs.
|
|
1400
1417
|
You must commit the generated file to your repository:
|
|
1401
1418
|
|
|
1402
|
-
git add ${toPosixPath(
|
|
1419
|
+
git add ${toPosixPath(serverFile)}
|
|
1403
1420
|
git commit -m "chore: update vercel api entry"
|
|
1404
1421
|
git push
|
|
1405
1422
|
|
|
1406
1423
|
Without this step, Vercel will not find your API routes.
|
|
1407
1424
|
`);
|
|
1408
1425
|
}
|
|
1409
|
-
if (platform === "
|
|
1426
|
+
if (platform === "deno") {
|
|
1427
|
+
const serverFile = path__default.default.relative(cwd, outFile);
|
|
1410
1428
|
console.log(`
|
|
1411
|
-
\x1B[90m${(/* @__PURE__ */ new Date()).toLocaleTimeString()}\x1B[0m \x1B[
|
|
1412
|
-
|
|
1413
|
-
|
|
1429
|
+
\x1B[90m${(/* @__PURE__ */ new Date()).toLocaleTimeString()}\x1B[0m \x1B[33m[vite]\x1B[0m \x1B[33m\u26A0\uFE0F Deno Deploy platform detected.\x1B[0m`);
|
|
1430
|
+
console.log(` Deno Deploy reads your server/ directory BEFORE the build step runs.
|
|
1431
|
+
You must commit the generated file to your repository:
|
|
1432
|
+
|
|
1433
|
+
git add ${toPosixPath(serverFile)}
|
|
1434
|
+
git commit -m "chore: update deno server entry"
|
|
1435
|
+
git push
|
|
1436
|
+
|
|
1437
|
+
Without this step, Deno Deploy will not find your API routes.
|
|
1438
|
+
`);
|
|
1439
|
+
console.log(` In Deno Console, set:
|
|
1440
|
+
- Entrypoint: ${toPosixPath(serverFile)}
|
|
1441
|
+
- Build Command: vite build
|
|
1442
|
+
- Runtime: Dynamic App
|
|
1414
1443
|
`);
|
|
1415
1444
|
}
|
|
1416
|
-
if (platform === "
|
|
1445
|
+
if (platform === "netlify") {
|
|
1446
|
+
const functionFile = path__default.default.relative(cwd, outFile);
|
|
1417
1447
|
console.log(`
|
|
1418
|
-
\x1B[90m${(/* @__PURE__ */ new Date()).toLocaleTimeString()}\x1B[0m \x1B[36m[vite]\x1B[0m \x1B[32m\u2713
|
|
1419
|
-
|
|
1420
|
-
|
|
1448
|
+
\x1B[90m${(/* @__PURE__ */ new Date()).toLocaleTimeString()}\x1B[0m \x1B[36m[vite]\x1B[0m \x1B[32m\u2713 Netlify function generated successfully\x1B[0m \x1B[90m${toPosixPath(functionFile)}\x1B[0m`);
|
|
1449
|
+
console.log(` The function is ready for deployment to Netlify Edge Functions.
|
|
1450
|
+
Make sure your netlify.toml has:
|
|
1451
|
+
|
|
1452
|
+
[functions]
|
|
1453
|
+
directory = "netlify/edge-functions"
|
|
1454
|
+
|
|
1421
1455
|
`);
|
|
1422
1456
|
}
|
|
1423
|
-
if (platform === "
|
|
1457
|
+
if (platform === "cloudflare") {
|
|
1458
|
+
const workerFile = path__default.default.relative(cwd, outFile);
|
|
1424
1459
|
console.log(`
|
|
1425
|
-
\x1B[90m${(/* @__PURE__ */ new Date()).toLocaleTimeString()}\x1B[0m \x1B[36m[vite]\x1B[0m \x1B[32m\u2713
|
|
1426
|
-
The
|
|
1460
|
+
\x1B[90m${(/* @__PURE__ */ new Date()).toLocaleTimeString()}\x1B[0m \x1B[36m[vite]\x1B[0m \x1B[32m\u2713 Cloudflare Worker generated successfully\x1B[0m \x1B[90m${toPosixPath(workerFile)}\x1B[0m`);
|
|
1461
|
+
console.log(` The worker includes SPA fallback for React Router.
|
|
1462
|
+
Make sure your wrangler.toml has the ASSETS binding configured.
|
|
1427
1463
|
`);
|
|
1428
1464
|
}
|
|
1429
1465
|
} catch (error) {
|