bun run dev keeps Vite's address in view: the backend's full log goes to a file

PocketBase prints about 230 lines of startup SQL, and the dev plugin forwarded every line to the
terminal, so Vite's "Local:" address scrolled out of view right after it appeared. The fourth fresh
reader of the README saw it happen.

Each dev backend's output now goes whole to .pb/logs/<name>.log (gitignored), and the terminal gets
only the lines a person must act on: errors, warnings, panics, and "address already in use", the
line the README and HOWTO tell a reader to watch for before setting DX_PB_PORT. The started line
names the log file. Tested against representative lines (in use, WARN, panic shown; SQL and the
banner quiet). The live check rides the next fresh-reader run.
This commit is contained in:
Doxa Maps 2026-09-29 13:15:59 +00:00
parent 6a1860539c
commit 0d147b275c

View file

@ -18,7 +18,7 @@
* DEV ONLY (`apply: 'serve'`). A production build never starts or bundles a backend.
*/
import { spawn } from 'node:child_process'
import { existsSync, mkdirSync, writeFileSync, rmSync, chmodSync, renameSync } from 'node:fs'
import { existsSync, mkdirSync, writeFileSync, rmSync, chmodSync, renameSync, createWriteStream } from 'node:fs'
import { createHash } from 'node:crypto'
import { connect } from 'node:net'
import { inflateRawSync } from 'node:zlib'
@ -153,9 +153,22 @@ function start(root, b, logger, restarts = 0) {
const child = spawn(resolve(root, b.bin), b.args || [], { cwd: root, env: { ...process.env, ...b.env }, stdio: ['ignore', 'pipe', 'pipe'] })
const entry = { child, restarts, logger }
REG.procs.set(b.name, entry)
const out = (d) => String(d).trim().split('\n').forEach((l) => l && entry.logger.info(`${tag} ${l}`))
// ★ THE FULL LOG GOES TO A FILE; THE TERMINAL GETS WHAT A PERSON MUST ACT ON. PocketBase prints about
// 230 lines of startup SQL, which scrolled Vite's "➜ Local:" address out of view for a fresh reader of the
// README. Every line still lands in .pb/logs/<name>.log (gitignored). Errors, warnings and "address already
// in use" (the line the README and HOWTO tell a reader to watch for before setting DX_PB_PORT) are
// forwarded exactly as before.
const logDir = resolve(root, '.pb', 'logs')
mkdirSync(logDir, { recursive: true })
const sink = createWriteStream(resolve(logDir, `${b.name}.log`), { flags: 'a' })
const LOUD = /\b(error|fail(ed|ure)?|panic|fatal|warn(ing)?|refused|denied|in use|cannot|unable)\b/i
const out = (d) => {
sink.write(d)
String(d).trim().split('\n').forEach((l) => l && LOUD.test(l) && entry.logger.info(`${tag} ${l}`))
}
child.stdout.on('data', out)
child.stderr.on('data', out)
child.on('exit', () => sink.end())
child.on('exit', (code) => {
if (REG.procs.get(b.name) !== entry) return
REG.procs.delete(b.name)
@ -170,7 +183,7 @@ function start(root, b, logger, restarts = 0) {
}, 2000)
}
})
logger.info(`${tag} started → ${b.route} on 127.0.0.1:${b.port}`)
logger.info(`${tag} started → ${b.route} on 127.0.0.1:${b.port} (full log: .pb/logs/${b.name}.log)`)
}
export function backendDevPlugin(root, env = process.env) {