tovuk 0.1.101

Deploy Rust workers, static frontends, and full-stack services to Tovuk.
mod layout;
mod predicates;
mod scripts;
mod source;

use super::check::{QualityCheck, quality_check};
use std::path::Path;

pub(crate) use layout::{
    frontend_build_command, frontend_check_command, frontend_package_manager, is_next_frontend,
    is_plain_static_frontend,
};
pub(crate) use predicates::{
    has_frontend_install_command, has_frontend_script_run, uses_javascript_linter,
};
use scripts::frontend_script_checks;
use source::frontend_source_checks;

pub(crate) fn static_frontend_checks(project_dir: &Path, run_scripts: bool) -> Vec<QualityCheck> {
    if is_plain_static_frontend(project_dir) {
        return Vec::new();
    }
    let mut checks = vec![frontend_lockfile_check(project_dir)];
    if let Some(check) = next_static_export_check(project_dir) {
        checks.push(check);
    }
    checks.extend(frontend_source_checks(project_dir));
    checks.extend(frontend_script_checks(project_dir, run_scripts));
    checks
}

fn frontend_lockfile_check(project_dir: &Path) -> QualityCheck {
    quality_check(
        "frontend lockfile",
        layout::frontend_lockfile_exists(project_dir),
        "found",
        "missing",
        "Commit package-lock.json, pnpm-lock.yaml, yarn.lock, bun.lock, or bun.lockb, then retry.",
    )
}

fn next_static_export_check(project_dir: &Path) -> Option<QualityCheck> {
    if !layout::is_next_frontend(project_dir) {
        return None;
    }
    let config_file = layout::next_config_file(project_dir);
    let ok = layout::next_static_export_enabled(project_dir);
    Some(QualityCheck {
        name: "Next static export".to_owned(),
        ok,
        message: next_static_export_message(config_file, ok),
        agent_instruction: if ok {
            None
        } else {
            Some(next_static_export_instruction(config_file))
        },
    })
}

fn next_static_export_message(config_file: Option<&str>, ok: bool) -> String {
    match (config_file, ok) {
        (Some(file), true) => format!("{file} sets output export"),
        (Some(file), false) => format!("{file} missing output export"),
        (None, _) => "next.config missing".to_owned(),
    }
}

fn next_static_export_instruction(config_file: Option<&str>) -> String {
    let target = config_file.unwrap_or("next.config.mjs");
    format!(
        "Create or update `{target}` with `output: \"export\"`, keep the Tovuk frontend output directory set to `out`, move Next API routes, middleware, SSR, and server logic to the Rust worker, then rerun `tovuk check --json`."
    )
}