Updated frontend running dev mode using deno/npm/bun as well. Additional dev mode Caddyfile to go https://localhost/.
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
localhost {
|
||||
# Forwards API by caddy, bypassing the Vite dev proxy
|
||||
# Avoids bug https://github.com/oven-sh/bun/issues/9882
|
||||
handle /api/* {
|
||||
reverse_proxy :4402 # directly to backend
|
||||
}
|
||||
handle {
|
||||
reverse_proxy :4403 # vite dev server
|
||||
}
|
||||
}
|
||||
+69
-21
@@ -1,8 +1,36 @@
|
||||
import atexit
|
||||
import shutil
|
||||
import signal
|
||||
import subprocess
|
||||
from importlib import resources
|
||||
from pathlib import Path
|
||||
from sys import stderr
|
||||
from threading import Thread
|
||||
|
||||
__all__ = ["path", "file", "run_dev"]
|
||||
|
||||
NO_FRONTEND_TOOL = """\
|
||||
┃ ⚠️ deno, npm or bunx needed to run the frontend server.
|
||||
"""
|
||||
|
||||
BUN_BUG = """\
|
||||
┃ ⚠️ Bun cannot correctly proxy API requests to the backend.
|
||||
┃ Bug report: https://github.com/oven-sh/bun/issues/9882
|
||||
┃
|
||||
┃ Options:
|
||||
┃ - sudo caddy run --config caddy/Caddyfile.dev
|
||||
┃ - Install deno or npm instead
|
||||
┃
|
||||
┃ Caddy will skip the Vite for API calls and serve everything at port 443.
|
||||
┃ Otherwise Vite serves at port 8077 and proxies to backend (broken with bun).
|
||||
"""
|
||||
|
||||
NO_FRONTEND = """\
|
||||
┃
|
||||
┃ Note: only static build of the frontend is served at port 8078.
|
||||
┃ The page will not update with frontend code changes.
|
||||
"""
|
||||
|
||||
|
||||
def _resolve_static_dir() -> Path:
|
||||
# Try packaged path via importlib.resources (works for wheel/installed).
|
||||
@@ -26,12 +54,7 @@ def file(*parts: str) -> Path:
|
||||
|
||||
|
||||
def run_dev():
|
||||
"""Spawn the frontend dev server (bun or npm) as a background process."""
|
||||
import atexit
|
||||
import shutil
|
||||
import signal
|
||||
import subprocess
|
||||
|
||||
"""Spawn the frontend dev server (deno, npm, or bunx) as a background process."""
|
||||
devpath = Path(__file__).parent.parent.parent / "frontend"
|
||||
if not (devpath / "package.json").exists():
|
||||
raise RuntimeError(
|
||||
@@ -39,22 +62,47 @@ def run_dev():
|
||||
if "site-packages" in devpath.parts
|
||||
else f"Frontend source code not found at {devpath}"
|
||||
)
|
||||
bun = shutil.which("bun")
|
||||
npm = shutil.which("npm") if bun is None else None
|
||||
if not bun and not npm:
|
||||
raise RuntimeError("Neither bun nor npm found on PATH for dev server")
|
||||
cmd: list[str] = [bun, "--bun", "run", "dev"] if bun else [npm, "run", "dev"] # type: ignore[list-item]
|
||||
proc = subprocess.Popen(cmd, cwd=str(devpath))
|
||||
|
||||
def _terminate():
|
||||
if proc.poll() is None:
|
||||
proc.terminate()
|
||||
options = [
|
||||
("deno", "run", "dev"),
|
||||
("npm", "run", "dev", "--"),
|
||||
("bunx", "--bun", "vite"),
|
||||
]
|
||||
cmd = None
|
||||
tool_name = None
|
||||
for option in options:
|
||||
if tool := shutil.which(option[0]):
|
||||
cmd = [tool, *option[1:]]
|
||||
tool_name = option[0]
|
||||
break
|
||||
|
||||
atexit.register(_terminate)
|
||||
vite_process = None
|
||||
|
||||
def _signal_handler(signum, frame):
|
||||
_terminate()
|
||||
raise SystemExit(0)
|
||||
def start_vite():
|
||||
nonlocal vite_process
|
||||
if cmd is None:
|
||||
stderr.write(NO_FRONTEND_TOOL)
|
||||
stderr.write(NO_FRONTEND)
|
||||
return
|
||||
assert tool_name is not None
|
||||
try:
|
||||
if tool_name == "bunx":
|
||||
stderr.write(BUN_BUG)
|
||||
|
||||
for sig in (signal.SIGINT, signal.SIGTERM):
|
||||
signal.signal(sig, _signal_handler)
|
||||
stderr.write(f">>> {' '.join([tool_name, *cmd[1:]])}\n")
|
||||
vite_process = subprocess.Popen(cmd, cwd=str(devpath), shell=False)
|
||||
except Exception as e:
|
||||
stderr.write(f"┃ ⚠️ Vite couldn't start: {e}\n")
|
||||
stderr.write(NO_FRONTEND)
|
||||
|
||||
def cleanup():
|
||||
vite_process.terminate()
|
||||
vite_process.wait()
|
||||
|
||||
# Start Vite in a separate thread
|
||||
vite_thread = Thread(target=start_vite, daemon=True)
|
||||
vite_thread.start()
|
||||
|
||||
atexit.register(cleanup)
|
||||
signal.signal(signal.SIGTERM, lambda *_: cleanup())
|
||||
signal.signal(signal.SIGINT, lambda *_: cleanup())
|
||||
|
||||
Reference in New Issue
Block a user