use crate::manifest::{parse_manifest, validate_manifest, AppManifest, AppType};
use anyhow::{bail, Context, Result};
use std::path::{Path, PathBuf};
pub fn run(project_path: &Path) -> Result<AppManifest> {
let project_path = project_path.canonicalize().with_context(|| {
format!("could not canonicalize project path '{}'", project_path.display())
})?;
let manifest_path = project_path.join("manifest.json");
if !manifest_path.exists() {
bail!(
"no manifest.json at {} — did you run `node-app new` first?",
manifest_path.display()
);
}
let manifest = parse_manifest(&manifest_path)?;
let is_apt_target = true;
validate_manifest(&manifest, is_apt_target)?;
let ep = manifest
.entrypoint
.clone()
.unwrap_or_else(|| match manifest.app_type {
AppType::Native => "app.so".into(),
AppType::Bun => "dist/index.js".into(),
AppType::Standalone => "app".into(),
});
let entry_full = project_path.join(&ep);
if !entry_full.exists() {
match manifest.app_type {
AppType::Bun => {
eprintln!(
"ℹ entrypoint '{}' not found yet (will be built during `package`)",
ep
);
}
AppType::Native => {
eprintln!(
"ℹ entrypoint '{}' not found yet (cdylib build artifact missing)",
ep
);
}
AppType::Standalone => {
eprintln!(
"ℹ entrypoint '{}' not found yet (binary build artifact missing; run `cargo build --release`)",
ep
);
}
}
}
if manifest.has_ui {
if let Some(ui) = &manifest.ui_path {
let ui_full: PathBuf = project_path.join(ui);
if !ui_full.is_dir() {
eprintln!(
"ℹ has_ui=true but {} is not yet a directory (build the UI before packaging)",
ui_full.display()
);
}
}
}
println!("✓ schema v{} valid", manifest.manifest_version);
println!("✓ all capabilities recognised");
if let Some(abi) = &manifest.abi {
println!("✓ ABI {} supported", abi);
} else {
println!("✓ ABI default (v1) supported");
}
println!("✓ tier policy ok ({} app)", match manifest.app_type {
AppType::Native => "native",
AppType::Bun => "bun",
AppType::Standalone => "standalone",
});
Ok(manifest)
}