diff --git a/TODO.txt b/TODO.txt index 24cef74..80e7bb5 100644 --- a/TODO.txt +++ b/TODO.txt @@ -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 diff --git a/apps/basic/package.json b/apps/basic/package.json index 6fe680b..e8be2ee 100644 --- a/apps/basic/package.json +++ b/apps/basic/package.json @@ -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" }, diff --git a/apps/profile/index.tsx b/apps/profile/index.tsx index a7920b0..62cb5cb 100644 --- a/apps/profile/index.tsx +++ b/apps/profile/index.tsx @@ -2,9 +2,6 @@ import { Hype } from 'hype' const app = new Hype -app.get('/', c => c.html(

Hi there!

)) - -const apps = () => { -} +app.get('/', c => c.html(

My Profile!!!

)) export default app.defaults diff --git a/apps/profile/package.json b/apps/profile/package.json index 6fe680b..e8be2ee 100644 --- a/apps/profile/package.json +++ b/apps/profile/package.json @@ -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" }, diff --git a/src/server/index.tsx b/src/server/index.tsx index 5d04e6e..2dba9cf 100644 --- a/src/server/index.tsx +++ b/src/server/index.tsx @@ -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() -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