use anyhow::{Context, Result};
use clap::Args;
use std::path::{Path, PathBuf};
pub use webui::CssStrategy;
pub use webui::DomStrategy;
pub use webui::LegalComments;
pub use webui::Plugin;
pub use webui::DEFAULT_ASSET_FILE_NAME_TEMPLATE;
#[derive(Args, Clone)]
pub struct AppArgs {
#[arg(default_value = ".")]
pub app: PathBuf,
#[arg(long, default_value = "index.html")]
pub entry: String,
#[arg(long, value_enum, default_value_t = CssStrategy::Link)]
pub css: CssStrategy,
#[arg(long, value_enum, default_value_t = DomStrategy::Shadow)]
pub dom: DomStrategy,
#[arg(long, value_enum)]
pub plugin: Option<Plugin>,
#[arg(long, value_name = "SOURCE")]
pub components: Vec<String>,
#[arg(long, default_value = DEFAULT_ASSET_FILE_NAME_TEMPLATE)]
pub asset_file_name_template: String,
#[arg(long)]
pub css_public_base: Option<String>,
#[arg(long, value_enum, default_value_t = LegalComments::Inline)]
pub legal_comments: LegalComments,
}
impl AppArgs {
pub fn to_build_options(&self, app_dir: &std::path::Path) -> webui::BuildOptions {
webui::BuildOptions {
app_dir: app_dir.to_path_buf(),
entry: self.entry.clone(),
css: self.css,
dom: self.dom,
plugin: self.plugin,
components: self.components.clone(),
component_asset_roots: Vec::new(),
css_file_name_template: self.asset_file_name_template.clone(),
css_public_base: self.css_public_base.clone(),
legal_comments: self.legal_comments,
theme: None,
}
}
}
pub fn load_theme(theme: &str, search_root: &Path) -> Result<webui::TokenFile> {
let resolved = webui::resolve_theme_path(theme, search_root)
.with_context(|| format!("Failed to resolve theme: {theme}"))?;
webui::load_token_file(&resolved)
.with_context(|| format!("Failed to load theme file: {}", resolved.display()))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn to_build_options_passes_asset_file_output_settings() {
let args = AppArgs {
app: std::path::PathBuf::from("."),
entry: "index.html".to_string(),
css: CssStrategy::Link,
dom: DomStrategy::Shadow,
plugin: None,
components: Vec::new(),
asset_file_name_template: "[name]-[hash].[ext]".to_string(),
css_public_base: Some("https://cdn.example.com/assets".to_string()),
legal_comments: LegalComments::None,
};
let options = args.to_build_options(std::path::Path::new("."));
assert_eq!(options.css_file_name_template, "[name]-[hash].[ext]");
assert_eq!(
options.css_public_base.as_deref(),
Some("https://cdn.example.com/assets")
);
assert!(options.component_asset_roots.is_empty());
assert_eq!(options.legal_comments, LegalComments::None);
}
#[test]
fn load_theme_resolves_packages_from_app_dir() {
let dir = tempfile::tempdir().unwrap();
let app_dir = dir.path().join("app");
let package_dir = app_dir.join("node_modules").join("@scope").join("tokens");
std::fs::create_dir_all(&package_dir).unwrap();
std::fs::write(
package_dir.join("tokens.json"),
r##"{"themes":{"light":{"color-brand":"#123456"}}}"##,
)
.unwrap();
let theme = load_theme("@scope/tokens", &app_dir).unwrap();
assert_eq!(theme.themes["light"]["color-brand"], "#123456");
}
}