use crate::ext::anyhow::{anyhow, bail, Context, Result};
use crate::{
ext::exe::{get_exe, Exe},
fs, Config,
};
use lightningcss::{
stylesheet::{MinifyOptions, ParserOptions, PrinterOptions, StyleSheet},
targets::Browsers,
};
use std::{path::Path, path::PathBuf};
use tokio::process::Command;
const DEST: &str = "target/site/pkg/app.css";
pub async fn run(config: &Config) -> Result<()> {
fs::create_dir_all("target/site/pkg").await.dot()?;
let style = &config.leptos.style;
let style_file = &style.file;
log::debug!("Style found: {style_file}");
let file = PathBuf::from(style_file);
let css_file = match file.extension().map(|ext| ext.to_str()).flatten() {
Some("sass") | Some("scss") => compile_sass(style_file, config.cli.release)
.await
.context(format!("compile sass/scss: {style_file}"))?,
Some("css") => {
fs::copy(style_file, DEST).await.dot().dot()?;
PathBuf::from(DEST)
}
_ => bail!("Not a css/sass/scss style file: {style_file}"),
};
let browsers = browser_lists(&style.browserquery).context("leptos.style.browserquery")?;
process_css(&css_file, browsers, config.cli.release)
.await
.context(format!("process css {style_file}"))?;
Ok(())
}
async fn compile_sass(scss_file: &str, release: bool) -> Result<PathBuf> {
let mut args = vec![scss_file, DEST];
release.then(|| args.push("--no-source-map"));
let exe = get_exe(Exe::Sass)
.await
.context("Try manually installing sass: https://sass-lang.com/install")?;
let mut cmd = Command::new(exe).args(&args).spawn()?;
cmd.wait()
.await
.context(format!("sass {}", args.join(" ")))?;
Ok(PathBuf::from(DEST))
}
fn browser_lists(query: &str) -> Result<Option<Browsers>> {
Browsers::from_browserslist([query]).context(format!("Error in browserlist query: {query}"))
}
async fn process_css(file: &Path, browsers: Option<Browsers>, release: bool) -> Result<()> {
let css = fs::read_to_string(&file).await?;
let mut style =
StyleSheet::parse(&css, ParserOptions::default()).map_err(|e| anyhow!("{e}"))?;
if release {
style.minify(MinifyOptions::default())?;
}
let mut options = PrinterOptions::default();
options.targets = browsers;
if release {
options.minify = true;
}
let style_output = style.to_css(options)?;
fs::write(&file, style_output.code.as_bytes()).await?;
Ok(())
}