From 15301f91263c6e4f7f2ed20c60a337b4a7546a8b Mon Sep 17 00:00:00 2001 From: Chris Wanstrath <2+defunkt@users.noreply.github.com> Date: Mon, 15 Sep 2025 14:18:53 -0700 Subject: [PATCH] tsx webapps and directories --- nose/app/dir/index.ts | 4 ++++ nose/app/dir/other.ts | 1 + nose/app/hello.ts | 2 +- nose/app/jsx.tsx | 2 ++ nose/app/ping.ts | 2 +- src/webapp.ts | 48 +++++++++++++++++++++++++++++++++++-------- tsconfig.json | 1 + 7 files changed, 50 insertions(+), 10 deletions(-) create mode 100644 nose/app/dir/index.ts create mode 100644 nose/app/dir/other.ts create mode 100644 nose/app/jsx.tsx diff --git a/nose/app/dir/index.ts b/nose/app/dir/index.ts new file mode 100644 index 0000000..b48fc6a --- /dev/null +++ b/nose/app/dir/index.ts @@ -0,0 +1,4 @@ +import { text } from "./other" + +export default (c: Context) => + c.text(text) \ No newline at end of file diff --git a/nose/app/dir/other.ts b/nose/app/dir/other.ts new file mode 100644 index 0000000..f9897b4 --- /dev/null +++ b/nose/app/dir/other.ts @@ -0,0 +1 @@ +export const text = "w00000t!" \ No newline at end of file diff --git a/nose/app/hello.ts b/nose/app/hello.ts index b0eca04..9dc6cd7 100644 --- a/nose/app/hello.ts +++ b/nose/app/hello.ts @@ -1,2 +1,2 @@ export default (c: Context) => - c.text("Hello, world!") \ No newline at end of file + "Hello, world!" \ No newline at end of file diff --git a/nose/app/jsx.tsx b/nose/app/jsx.tsx new file mode 100644 index 0000000..05c75ff --- /dev/null +++ b/nose/app/jsx.tsx @@ -0,0 +1,2 @@ +export default (c: Context) => + Very cool! \ No newline at end of file diff --git a/nose/app/ping.ts b/nose/app/ping.ts index f6946da..ea03a4c 100644 --- a/nose/app/ping.ts +++ b/nose/app/ping.ts @@ -1,2 +1,2 @@ export default (c: Context) => - c.text("pong") \ No newline at end of file + "pong" \ No newline at end of file diff --git a/src/webapp.ts b/src/webapp.ts index 20ba8e2..1be7616 100644 --- a/src/webapp.ts +++ b/src/webapp.ts @@ -1,25 +1,57 @@ import type { Context } from "hono" +import type { Child } from "hono/jsx" import { join } from "node:path" import { NOSE_APP } from "./config" import { isFile } from "./utils" +type App = (r: Context) => string | Child | Response + export async function serveApp(c: Context, subdomain: string): Promise { const app = await findApp(subdomain) if (app) - return app(c) + return await toResponse(app(c)) return c.text(`App Not Found: ${subdomain}`, 404) } -async function findApp(name: string): Promise<((r: Context) => Response) | undefined> { - const path = join(NOSE_APP, `${name}.ts`) +async function findApp(name: string): Promise { + let path = join(NOSE_APP, `${name}.ts`) + let app = await loadApp(path) + if (app) return app - if (isFile(path)) { - const mod = await import(path + `?t=${Date.now()}`) - if (mod?.default) - return mod.default as (r: Context) => Response - } + path = join(NOSE_APP, `${name}.tsx`) + app = await loadApp(path) + if (app) return app + + path = join(NOSE_APP, name, "index.ts") + app = await loadApp(path) + if (app) return app + + path = join(NOSE_APP, name, "index.tsx") + app = await loadApp(path) + if (app) return app console.error("can't find app:", name) } + +async function loadApp(path: string): Promise { + if (!isFile(path)) return + + const mod = await import(path + `?t=${Date.now()}`) + if (mod?.default) + return mod.default as App +} + +async function toResponse(source: string | Child | Response): Promise { + if (source instanceof Response) + return source + else if (typeof source === "string") + return new Response(source) + else + return new Response(await source?.toString(), { + headers: { + "Content-Type": "text/html; charset=utf-8" + } + }) +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index bfa0fea..545396c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,6 +6,7 @@ "module": "Preserve", "moduleDetection": "force", "jsx": "react-jsx", + "jsxImportSource": "hono/jsx", "allowJs": true, // Bundler mode