it's alive
This commit is contained in:
parent
465ed69fc3
commit
45286920de
|
|
@ -1,7 +1,7 @@
|
||||||
import { text } from "./other"
|
import { text } from "./other"
|
||||||
import { css } from "@utils"
|
import { css } from "@utils"
|
||||||
|
|
||||||
export default (c: Context) => {
|
export default () => {
|
||||||
return <>
|
return <>
|
||||||
{css`
|
{css`
|
||||||
body {
|
body {
|
||||||
|
|
|
||||||
29
nose/app/routes.tsx
Normal file
29
nose/app/routes.tsx
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { routes } from "@utils"
|
||||||
|
|
||||||
|
export default routes({
|
||||||
|
"GET /": index,
|
||||||
|
"GET /pets": pets
|
||||||
|
})
|
||||||
|
|
||||||
|
function index() {
|
||||||
|
return <>
|
||||||
|
<h1>Hi world!</h1>
|
||||||
|
<p>Welcome to my personal web page.</p>
|
||||||
|
<p>If you are looking for information on pets, click here:</p>
|
||||||
|
<p><a href="/pets">PETS</a></p>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
|
||||||
|
function pets(c: Context) {
|
||||||
|
return c.html(<>
|
||||||
|
<ul>
|
||||||
|
<li>dogs</li>
|
||||||
|
<li>cats</li>
|
||||||
|
<li>iguanas</li>
|
||||||
|
<li>hamsters</li>
|
||||||
|
<li>snakes</li>
|
||||||
|
<li>chickens</li>
|
||||||
|
<li>...even goats!</li>
|
||||||
|
</ul>
|
||||||
|
</>)
|
||||||
|
}
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
{
|
{
|
||||||
"name": "pluto",
|
"name": "pluto",
|
||||||
"module": "index.ts",
|
"module": "src/server.tsx",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "bun src/server.ts",
|
"start": "bun src/server.tsx",
|
||||||
"dev": "bun --hot src/server.ts"
|
"dev": "bun --hot src/server.tsx"
|
||||||
},
|
},
|
||||||
"alias": {
|
"alias": {
|
||||||
"@utils": "./src/utils.tsx",
|
"@utils": "./src/utils.tsx",
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import color from "kleur"
|
||||||
|
|
||||||
import { NOSE_ICON, NOSE_DIR, NOSE_BIN, NOSE_APP } from "./config"
|
import { NOSE_ICON, NOSE_DIR, NOSE_BIN, NOSE_APP } from "./config"
|
||||||
import { transpile, isFile } from "./utils"
|
import { transpile, isFile } from "./utils"
|
||||||
import { serveApp } from "./webapp"
|
import { apps, serveApp } from "./webapp"
|
||||||
|
|
||||||
//
|
//
|
||||||
// Hono setup
|
// Hono setup
|
||||||
|
|
@ -43,12 +43,29 @@ app.get("/js/:path{.+}", async c => {
|
||||||
// app routes
|
// app routes
|
||||||
//
|
//
|
||||||
|
|
||||||
app.get("*", async c => {
|
app.use("*", async (c, next) => {
|
||||||
const url = new URL(c.req.url)
|
const url = new URL(c.req.url)
|
||||||
const domains = url.hostname.split(".")
|
const domains = url.hostname.split(".")
|
||||||
const subdomain = domains.length > 1 ? domains[0]! : "none"
|
const subdomain = domains.length > 1 ? domains[0]! : ""
|
||||||
|
|
||||||
return await serveApp(c, subdomain)
|
if (subdomain) {
|
||||||
|
const app = serveApp(c, subdomain)
|
||||||
|
return await app
|
||||||
|
}
|
||||||
|
|
||||||
|
return next()
|
||||||
|
})
|
||||||
|
|
||||||
|
app.get("/", c => {
|
||||||
|
const url = new URL(c.req.url)
|
||||||
|
const domain = url.hostname
|
||||||
|
const port = url.port
|
||||||
|
|
||||||
|
return c.html(<>
|
||||||
|
<h1>apps</h1>
|
||||||
|
<ul>{apps().map(app => <li><a href={`http://${app}.${domain}:${port}`}>{app}</a></li>)}
|
||||||
|
</ul>
|
||||||
|
</>)
|
||||||
})
|
})
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
|
import { Hono } from "hono"
|
||||||
import { statSync } from "node:fs"
|
import { statSync } from "node:fs"
|
||||||
import { stat } from "node:fs/promises"
|
import { stat } from "node:fs/promises"
|
||||||
|
import { type Handler, toResponse } from "./webapp"
|
||||||
|
|
||||||
// Is the given `path` a file?
|
// Is the given `path` a file?
|
||||||
export function isFile(path: string): boolean {
|
export function isFile(path: string): boolean {
|
||||||
|
|
@ -26,7 +28,6 @@ export function randomID(): string {
|
||||||
return Math.random().toString(36).slice(2, 10)
|
return Math.random().toString(36).slice(2, 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const transpiler = new Bun.Transpiler({ loader: 'tsx' })
|
const transpiler = new Bun.Transpiler({ loader: 'tsx' })
|
||||||
const transpileCache: Record<string, string> = {}
|
const transpileCache: Record<string, string> = {}
|
||||||
|
|
||||||
|
|
@ -65,3 +66,25 @@ export function js(strings: TemplateStringsArray, ...values: any[]) {
|
||||||
}, '')
|
}, '')
|
||||||
}} />
|
}} />
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// for defining routes in your NOSE webapp
|
||||||
|
// example:
|
||||||
|
// export default routes({
|
||||||
|
// "GET /": index,
|
||||||
|
// "GET /pets": pets
|
||||||
|
// })
|
||||||
|
export function routes(def: Record<string, Handler>): Hono {
|
||||||
|
const app = new Hono
|
||||||
|
|
||||||
|
for (const key in def) {
|
||||||
|
const parts = key.split(" ") // GET /path
|
||||||
|
const method = parts[0] || "GET"
|
||||||
|
const path = parts[1] || "/"
|
||||||
|
|
||||||
|
console.log(method, path, def[key])
|
||||||
|
//@ts-ignore
|
||||||
|
app.on(method, path, async c => toResponse(await def[key](c)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return app
|
||||||
|
}
|
||||||
|
|
@ -1,18 +1,32 @@
|
||||||
import type { Context } from "hono"
|
import { type Context, Hono } from "hono"
|
||||||
import type { Child } from "hono/jsx"
|
import type { Child } from "hono/jsx"
|
||||||
|
import { renderToString } from "hono/jsx/dom/server"
|
||||||
import { join } from "node:path"
|
import { join } from "node:path"
|
||||||
|
import { readdirSync } from "node:fs"
|
||||||
import { NOSE_APP } from "./config"
|
import { NOSE_APP } from "./config"
|
||||||
import { isFile } from "./utils"
|
import { isFile } from "./utils"
|
||||||
|
|
||||||
type App = (r: Context) => string | Child | Response
|
export type Handler = (r: Context) => string | Child | Response | Promise<Response>
|
||||||
|
export type App = Hono | Handler
|
||||||
|
|
||||||
export async function serveApp(c: Context, subdomain: string): Promise<Response> {
|
export async function serveApp(c: Context, subdomain: string): Promise<Response> {
|
||||||
const app = await findApp(subdomain)
|
const app = await findApp(subdomain)
|
||||||
|
|
||||||
if (app)
|
if (!app) return c.text(`App Not Found: ${subdomain}`, 404)
|
||||||
return await toResponse(app(c))
|
|
||||||
|
|
||||||
return c.text(`App Not Found: ${subdomain}`, 404)
|
if (app instanceof Hono)
|
||||||
|
return app.fetch(c.req.raw)
|
||||||
|
else
|
||||||
|
return toResponse(app(c))
|
||||||
|
}
|
||||||
|
|
||||||
|
export function apps(): string[] {
|
||||||
|
const apps: string[] = []
|
||||||
|
|
||||||
|
for (const entry of readdirSync(NOSE_APP))
|
||||||
|
apps.push(entry.replace(/\.tsx?/, ""))
|
||||||
|
|
||||||
|
return apps
|
||||||
}
|
}
|
||||||
|
|
||||||
async function findApp(name: string): Promise<App | undefined> {
|
async function findApp(name: string): Promise<App | undefined> {
|
||||||
|
|
@ -43,13 +57,13 @@ async function loadApp(path: string): Promise<App | undefined> {
|
||||||
return mod.default as App
|
return mod.default as App
|
||||||
}
|
}
|
||||||
|
|
||||||
async function toResponse(source: string | Child | Response): Promise<Response> {
|
export function toResponse(source: string | Child | Response): Response {
|
||||||
if (source instanceof Response)
|
if (source instanceof Response)
|
||||||
return source
|
return source
|
||||||
else if (typeof source === "string")
|
else if (typeof source === "string")
|
||||||
return new Response(source)
|
return new Response(source)
|
||||||
else
|
else
|
||||||
return new Response(await source?.toString(), {
|
return new Response(renderToString(source), {
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "text/html; charset=utf-8"
|
"Content-Type": "text/html; charset=utf-8"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user