use crate::error::WasmError;
use std::collections::HashMap;
use wasm_bindgen::prelude::*;
use webui_parser::plugin::webui::WebUIParserPlugin;
use webui_parser::{CssStrategy, HtmlParser};
use webui_protocol::WebUIProtocol;
#[wasm_bindgen]
pub fn build_protocol(files: JsValue, entry: &str) -> Result<Vec<u8>, JsValue> {
let files_map: HashMap<String, String> =
serde_wasm_bindgen::from_value(files).map_err(|e| JsValue::from_str(&e.to_string()))?;
build_protocol_inner(&files_map, entry).map_err(|e| JsValue::from_str(&e.to_string()))
}
pub(crate) fn build_protocol_inner(
files: &HashMap<String, String>,
entry: &str,
) -> Result<Vec<u8>, WasmError> {
let protocol = parse_to_protocol(files, entry)?;
protocol.to_protobuf().map_err(WasmError::Protocol)
}
fn register_components(
parser: &mut HtmlParser,
files: &HashMap<String, String>,
entry: &str,
) -> Result<(), WasmError> {
for (filename, content) in files {
if filename != entry && filename.ends_with(".html") {
let tag_name = filename.trim_end_matches(".html");
if tag_name.contains('-') {
let css_key = format!("{tag_name}.css");
let css = files.get(&css_key).map(String::as_str);
parser.component_registry_mut().register_component(
tag_name,
content,
css,
has_script(files, tag_name),
)?;
}
}
}
Ok(())
}
fn has_script(files: &HashMap<String, String>, tag_name: &str) -> bool {
files.contains_key(&format!("{tag_name}.ts")) || files.contains_key(&format!("{tag_name}.js"))
}
pub(crate) fn parse_to_protocol(
files: &HashMap<String, String>,
entry: &str,
) -> Result<WebUIProtocol, WasmError> {
let entry_html = files
.get(entry)
.ok_or_else(|| WasmError::MissingEntry(entry.to_string()))?;
let mut parser =
HtmlParser::with_plugin_options(Box::new(WebUIParserPlugin::new()), CssStrategy::Style);
register_components(&mut parser, files, entry)?;
parser.parse(entry, entry_html)?;
parser.take_plugin_artifacts()?;
Ok(WebUIProtocol::new(parser.into_fragment_records()))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn build_protocol_reports_missing_entry() {
let files = HashMap::new();
let err = build_protocol_inner(&files, "index.html").unwrap_err();
assert_eq!(err.to_string(), "Entry file 'index.html' not found");
}
}