node-app-build 6.5.0

Mini app developer CLI: scaffold, validate, package node-app-* Debian packages
use std::path::{Path, PathBuf};

use anyhow::{bail, Result};
use serde_json::Value;

pub(crate) fn project_dir(project_root: &Path, manifest: &Value) -> PathBuf {
    let canonical = project_root.join("ui");
    if canonical.join("package.json").is_file() {
        return canonical;
    }

    manifest
        .get("ui_path")
        .and_then(Value::as_str)
        .and_then(|path| path.strip_suffix("/dist"))
        .map(|path| project_root.join(path))
        .unwrap_or(canonical)
}

pub(crate) fn destination_relative(manifest: &Value) -> PathBuf {
    manifest
        .get("ui_path")
        .and_then(Value::as_str)
        .map(PathBuf::from)
        .unwrap_or_else(|| PathBuf::from("ui/dist"))
}

pub(crate) fn source_dir(project_root: &Path, manifest: &Value) -> Result<PathBuf> {
    let declared = project_root.join(destination_relative(manifest));
    if declared.is_dir() {
        return Ok(declared);
    }

    let canonical = project_root.join("ui/dist");
    if canonical.is_dir() {
        return Ok(canonical);
    }

    bail!(
        "UI build produced no bundle; checked declared path {} and canonical path {}",
        declared.display(),
        canonical.display()
    )
}

#[cfg(test)]
mod tests {
    use std::path::PathBuf;

    use super::*;

    #[test]
    fn legacy_dist_destination_uses_ui_dist_source() {
        let project = tempfile::tempdir().unwrap();
        std::fs::create_dir_all(project.path().join("ui/dist")).unwrap();
        let manifest = serde_json::json!({"has_ui": true, "ui_path": "dist"});

        assert_eq!(
            source_dir(project.path(), &manifest).unwrap(),
            project.path().join("ui/dist")
        );
        assert_eq!(destination_relative(&manifest), PathBuf::from("dist"));
    }

    #[test]
    fn declared_source_wins_when_it_exists() {
        let project = tempfile::tempdir().unwrap();
        std::fs::create_dir_all(project.path().join("custom-ui")).unwrap();
        std::fs::create_dir_all(project.path().join("ui/dist")).unwrap();
        let manifest = serde_json::json!({"has_ui": true, "ui_path": "custom-ui"});

        assert_eq!(
            source_dir(project.path(), &manifest).unwrap(),
            project.path().join("custom-ui")
        );
    }

    #[test]
    fn missing_source_reports_declared_and_canonical_paths() {
        let project = tempfile::tempdir().unwrap();
        let manifest = serde_json::json!({"has_ui": true, "ui_path": "dist"});
        let error = source_dir(project.path(), &manifest)
            .unwrap_err()
            .to_string();

        assert!(error.contains(&project.path().join("dist").display().to_string()));
        assert!(error.contains(&project.path().join("ui/dist").display().to_string()));
    }
}