diff --git a/src/dev/entrypoint.rs b/src/dev/entrypoint.rs index 5266f9c..ba5e35a 100644 --- a/src/dev/entrypoint.rs +++ b/src/dev/entrypoint.rs @@ -14,27 +14,66 @@ struct EntrypointParamsResponse { entrypoint_params: Value, } -pub fn locate_html_entrypoint(upload_dir: &Path) -> Option { +pub fn locate_html_entrypoint(upload_dir: &Path) -> Result> { + // `index.html` always wins if present — that's the Web convention and + // sidesteps ambiguity when game exporters also emit helper HTML (debug + // shells, fullscreen wrappers, etc.). let default_index = upload_dir.join("index.html"); if default_index.is_file() { - return Some(default_index); + return Ok(Some(default_index)); } + // Only look at the upload_dir's top-level — a Godot/Unity/etc. export + // puts its entrypoint at the root of the upload folder, and recursing + // would flag engine-emitted subdir HTML (e.g. Unity's `TemplateData/`) + // as an ambiguity. + let mut found: Vec = Vec::new(); for entry in WalkDir::new(upload_dir) .min_depth(1) + .max_depth(1) .into_iter() .filter_map(|e| e.ok()) { - if entry.file_type().is_file() { - if let Some(ext) = entry.path().extension() { - if ext.eq_ignore_ascii_case("html") { - return Some(entry.into_path()); - } + if !entry.file_type().is_file() { + continue; + } + let is_html = entry + .path() + .extension() + .is_some_and(|ext| ext.eq_ignore_ascii_case("html") || ext.eq_ignore_ascii_case("htm")); + if is_html { + found.push(entry.into_path()); + // Two is enough to know it's ambiguous — but keep collecting so + // the error message lists every offender (capped at a handful). + if found.len() >= 8 { + break; } } } - None + match found.len() { + 0 => Ok(None), + 1 => Ok(Some(found.remove(0))), + _ => { + let mut names: Vec = found + .iter() + .map(|p| { + p.strip_prefix(upload_dir) + .unwrap_or(p) + .display() + .to_string() + }) + .collect(); + names.sort(); + anyhow::bail!( + "Multiple HTML files found in upload_dir ({}):\n - {}\n\n\ + Name one of them `index.html`, or remove the extras (often \ + stale files from a previous export with a different name).", + upload_dir.display(), + names.join("\n - ") + ) + } + } } pub async fn fetch_entrypoint_params(engine: &str, engine_version: &str, html_path: &Path) -> Result { diff --git a/src/dev/mod.rs b/src/dev/mod.rs index 47448d8..06ea38e 100644 --- a/src/dev/mod.rs +++ b/src/dev/mod.rs @@ -122,7 +122,7 @@ pub async fn handle_dev(config_path: Option, verbose: bool, no_open: bo // Validate required files exist in upload directory FileStaging::prepare(&upload_dir, &wavedash_config)?; - let html_entrypoint = locate_html_entrypoint(&upload_dir); + let html_entrypoint = locate_html_entrypoint(&upload_dir)?; let engine_version = wavedash_config.engine_version(); let entrypoint_params = match engine_kind { Some(EngineKind::Godot | EngineKind::Unity) => {