This commit is contained in:
Chris Wanstrath 2026-01-27 14:53:05 -08:00
parent 1ec012338f
commit fa459c45eb
5 changed files with 43 additions and 21 deletions

View File

@ -6,7 +6,8 @@
[x] scans for apps/**/package.json, scripts.toes
[x] runs that for each, giving it a PORT
[x] has GET / page that shows all the running apps/status/port
[ ] watch each app and restart it on update
[x] watch each app and restart it on update
[x] watches for and adds/removes apps
## cli

View File

@ -4,7 +4,7 @@
"type": "module",
"private": true,
"scripts": {
"toes": "bun start",
"toes": "bun run --watch index.tsx",
"start": "bun run index.tsx",
"dev": "bun run --hot index.tsx"
},

View File

@ -2,9 +2,6 @@ import { Hype } from 'hype'
const app = new Hype
app.get('/', c => c.html(<h1>Hi there!</h1>))
const apps = () => {
}
app.get('/', c => c.html(<h1>My Profile!!!</h1>))
export default app.defaults

View File

@ -4,7 +4,7 @@
"type": "module",
"private": true,
"scripts": {
"toes": "bun start",
"toes": "bun run --watch index.tsx",
"start": "bun run index.tsx",
"dev": "bun run --hot index.tsx"
},

View File

@ -1,6 +1,6 @@
import { Subprocess } from 'bun'
import type { Subprocess } from 'bun'
import { Hype } from 'hype'
import { readdirSync, readFileSync } from 'fs'
import { existsSync, readdirSync, readFileSync, watch } from 'fs'
import { join } from 'path'
const APPS_DIR = join(process.env.DATA_DIR ?? '.', 'apps')
@ -13,14 +13,14 @@ type RunningApp = {
const runningApps = new Map<string, RunningApp>()
const err = (app: string, ...msg: string[]) =>
const err = (app: string, ...msg: string[]) =>
console.error('🐾', `${app}:`, ...msg)
const info = (app: string, ...msg: string[]) =>
const info = (app: string, ...msg: string[]) =>
console.log('🐾', `${app}:`, ...msg)
const log = (app: string, ...msg: string[]) =>
console.log('🐾', `${app}»`, ...msg)
const log = (app: string, ...msg: string[]) =>
console.log(`<${app}>`, ...msg)
const appNames = () => {
return readdirSync(APPS_DIR, { withFileTypes: true })
@ -67,16 +67,20 @@ const loadApp = (dir: string) => {
}
}
const runApp = (dir: string, port: number) => {
const runApp = async (dir: string, port: number) => {
const pkg = loadApp(dir)
if (!pkg.scripts?.toes) return
const cwd = join(APPS_DIR, dir)
const cmd = ['bun', 'run', 'toes']
const needsInstall = !existsSync(join(cwd, 'node_modules'))
if (needsInstall) info(dir, 'Installing dependencies...')
const install = Bun.spawn(['bun', 'install'], { cwd, stdout: 'pipe', stderr: 'pipe' })
await install.exited
info(dir, `Starting on port ${port}...`)
const proc = Bun.spawn(cmd, {
const proc = Bun.spawn(['bun', 'run', 'toes'], {
cwd,
env: { ...process.env, PORT: String(port) },
stdout: 'pipe',
@ -105,15 +109,15 @@ const runApp = (dir: string, port: number) => {
// Handle process exit
proc.exited.then(code => {
if (code !== 0)
if (code !== 0)
err(dir, `Exited with code ${code}`)
else
else
info(dir, 'Stopped')
runningApps.delete(dir)
})
}
const getRunningApps = () =>
const getRunningApps = () =>
Array.from(runningApps.values()).map(({ name, port }) => ({ name, port }))
const stopApp = (dir: string) => {
@ -124,9 +128,27 @@ const stopApp = (dir: string) => {
}
}
console.log('🐾 Toes!')
runApps()
const watchAppsDir = () => {
watch(APPS_DIR, (event, filename) => {
console.log('apps dir')
if (!filename) return
if (isApp(filename) && !runningApps.has(filename)) {
const port = getPort()
runApp(filename, port)
}
if (runningApps.has(filename) && !isApp(filename))
stopApp(filename)
})
}
const startup = () => {
console.log('🐾 Toes!')
runApps()
watchAppsDir()
}
const app = new Hype()
@ -139,5 +161,7 @@ app.get('/', c => {
)
})
startup()
export { getRunningApps, stopApp }
export default app.defaults