start/restart webapps

This commit is contained in:
Chris Wanstrath 2025-10-10 15:54:43 -07:00
parent 9ce2995e0a
commit 6361316f14
2 changed files with 34 additions and 5 deletions

View File

@ -238,7 +238,7 @@ if (process.env.NODE_ENV === "production") {
await initNoseDir() await initNoseDir()
initCommands() initCommands()
initWebapps() await initWebapps()
initSneakers() initSneakers()
console.log(color.cyan(NOSE_ICON)) console.log(color.cyan(NOSE_ICON))

View File

@ -15,8 +15,10 @@ export type Handler = (r: Context) => string | Child | Response | Promise<Respon
export type App = Hono | Handler export type App = Hono | Handler
const processes = new Map<string, { port: string, proc: ReturnType<typeof Bun.spawn> }>() const processes = new Map<string, { port: string, proc: ReturnType<typeof Bun.spawn> }>()
const restarting = new Set<string>()
export function initWebapps() { export async function initWebapps() {
await startSubprocs()
startWatcher() startWatcher()
} }
@ -45,10 +47,11 @@ export async function serveApp(c: Context, subdomain: string): Promise<Response>
} }
async function startApp(name: string): Promise<string | undefined> { async function startApp(name: string): Promise<string | undefined> {
if (isStaticApp(name)) return
const existing = processes.get(name) const existing = processes.get(name)
if (existing) return existing.port if (existing) return existing.port
const port = String(4000 + processes.size) const port = String(4000 + processes.size)
const proc = Bun.spawn({ const proc = Bun.spawn({
cmd: [BUN_BIN, "run", "src/webapp/worker.ts", name], cmd: [BUN_BIN, "run", "src/webapp/worker.ts", name],
@ -58,9 +61,9 @@ async function startApp(name: string): Promise<string | undefined> {
}) })
processes.set(name, { port, proc }) processes.set(name, { port, proc })
proc.exited.then(() => processes.delete(name)) proc.exited.then(() => restartApp(name))
await Bun.sleep(50) await Bun.sleep(100) // give it time before first request
return port return port
} }
@ -83,6 +86,28 @@ async function shutdown() {
process.exit(0) process.exit(0)
} }
async function restartApp(name: string) {
if (restarting.has(name)) return
restarting.add(name)
console.log(`[child:${name}]`, "restarting")
const existing = processes.get(name)
if (existing) {
try { existing.proc.kill() } catch { }
processes.delete(name)
}
await Bun.sleep(200) // give bun time to release the port
restarting.delete(name)
await startApp(name)
}
async function startSubprocs() {
const list = apps()
await Promise.all(list.map(app => startApp(app)))
}
let wwwWatcher let wwwWatcher
function startWatcher() { function startWatcher() {
if (!expectDir(NOSE_DIR)) return if (!expectDir(NOSE_DIR)) return
@ -90,6 +115,10 @@ function startWatcher() {
wwwWatcher = watch(NOSE_DIR, { recursive: true }, async (event, filename) => { wwwWatcher = watch(NOSE_DIR, { recursive: true }, async (event, filename) => {
if (!filename) return if (!filename) return
const [appName,] = filename.split("/")
if (appName && !filename.includes("/pub/"))
restartApp(appName)
if (/^.+\/index\.tsx?$/.test(filename)) if (/^.+\/index\.tsx?$/.test(filename))
sendAll({ type: "apps", data: apps() }) sendAll({ type: "apps", data: apps() })
}) })