"""Build hook that ships the compiled web UI without breaking `pip install -e .`. interface/web/dist is gitignored - it is a Vite build artifact, not source - so a fresh clone does not have it. A static force-include of a missing path aborts the build, which would make the README's first step ("pip install -e .") fail before the reader ever gets to `npm run build`. So the include is decided here instead: * editable install -> skip a missing dist, the UI just isn't served yet * wheel / sdist -> hard error naming the exact command to run Set NEXUS_ALLOW_UILESS_BUILD=1 to build a deliberately headless distribution (API and CLI only). """ from __future__ import annotations import os from pathlib import Path from hatchling.builders.hooks.plugin.interface import BuildHookInterface _UI_SOURCE = Path("interface") / "web" / "dist" _UI_TARGETS = { "wheel": "synapse/_resources/web", "sdist": "interface/web/dist", } class NexusBuildHook(BuildHookInterface): PLUGIN_NAME = "custom" def initialize(self, version: str, build_data: dict) -> None: target = _UI_TARGETS.get(self.target_name) if target is None: return source = Path(self.root) / _UI_SOURCE if (source / "index.html").is_file(): build_data.setdefault("force_include", {})[str(source)] = target return if version == "editable" or os.getenv("NEXUS_ALLOW_UILESS_BUILD") == "1": self.app.display_warning( f"No compiled web UI at {_UI_SOURCE} - the backend will serve the " "API only. Build it with: cd interface/web && npm ci && npm run build" ) return raise RuntimeError( f"Cannot build a {self.target_name}: the compiled web UI is missing from " f"{_UI_SOURCE}.\n" "Build it first:\n" " cd interface/web && npm ci && npm run build\n" "Or set NEXUS_ALLOW_UILESS_BUILD=1 to ship an API/CLI-only distribution." )