slave 0.5.0

CLI for slave, a Laravel-inspired Rust framework for MCP-first apps: scaffold an MCP backend with auth, roles and Turso migrations, add Axum or Dioxus web apps, and route them by domain or path
//! Building Dioxus single-page apps (`make:app --stack dioxus`) with `dx`.

use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};

use anyhow::{Context, Result, bail};

/// How to get `dx`, matching the Dioxus version the template uses.
pub const INSTALL_HINT: &str = "install it with `cargo install dioxus-cli --version ^0.7 --locked`";

/// Whether `apps/<app>` is a Dioxus app.
pub fn is_dioxus_app(app_dir: &Path) -> bool {
    app_dir.join("Dioxus.toml").is_file()
}

pub fn dx_installed() -> bool {
    Command::new("dx")
        .arg("--version")
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .status()
        .is_ok_and(|status| status.success())
}

/// Builds the app's WebAssembly bundle and returns the directory to serve.
/// `base_path` (e.g. `/console`) makes its router and assets live under it.
pub fn build_spa(
    root: &Path,
    target_dir: &Path,
    app: &str,
    base_path: Option<&str>,
    release: bool,
) -> Result<PathBuf> {
    if !dx_installed() {
        bail!("`{app}` is a Dioxus app, but `dx` was not found; {INSTALL_HINT}");
    }
    let mut command = Command::new("dx");
    command
        .args(["build", "--platform", "web", "--package", app])
        .current_dir(root);
    if let Some(base_path) = base_path {
        command.args(["--base-path", base_path.trim_start_matches('/')]);
    }
    if release {
        command.arg("--release");
    }
    let status = command
        .status()
        .with_context(|| format!("failed to run `dx build` for {app}"))?;
    if !status.success() {
        bail!("`dx build` failed for {app}");
    }
    let profile = if release { "release" } else { "debug" };
    let public = target_dir
        .join("dx")
        .join(app)
        .join(profile)
        .join("web")
        .join("public");
    if !public.join("index.html").is_file() {
        bail!(
            "`dx build` finished but {} has no index.html",
            public.display()
        );
    }
    Ok(public)
}