61 lines
1.9 KiB
Python
61 lines
1.9 KiB
Python
from importlib import resources
|
|
from pathlib import Path
|
|
|
|
__all__ = ["path", "file", "run_dev"]
|
|
|
|
|
|
def _resolve_static_dir() -> Path:
|
|
# Try packaged path via importlib.resources (works for wheel/installed).
|
|
try: # pragma: no cover - trivial path resolution
|
|
pkg_dir = resources.files("passkey") / "frontend-build"
|
|
fs_path = Path(str(pkg_dir))
|
|
if fs_path.is_dir():
|
|
return fs_path
|
|
except Exception: # pragma: no cover - defensive
|
|
pass
|
|
# Fallback for editable/development before build.
|
|
return Path(__file__).parent.parent / "frontend-build"
|
|
|
|
|
|
path: Path = _resolve_static_dir()
|
|
|
|
|
|
def file(*parts: str) -> Path:
|
|
"""Return a child path under the static root."""
|
|
return path.joinpath(*parts)
|
|
|
|
|
|
def run_dev():
|
|
"""Spawn the frontend dev server (bun or npm) as a background process."""
|
|
import atexit
|
|
import shutil
|
|
import signal
|
|
import subprocess
|
|
|
|
devpath = Path(__file__).parent.parent.parent / "frontend"
|
|
if not (devpath / "package.json").exists():
|
|
raise RuntimeError(
|
|
"Dev frontend is only available when running from git."
|
|
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()
|
|
|
|
atexit.register(_terminate)
|
|
|
|
def _signal_handler(signum, frame):
|
|
_terminate()
|
|
raise SystemExit(0)
|
|
|
|
for sig in (signal.SIGINT, signal.SIGTERM):
|
|
signal.signal(sig, _signal_handler)
|