use proc_macro::Span;
use proc_macro_error::{abort_call_site, emit_error};
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use std::error::Error;
use std::fs::read_to_string;
use std::io::Write;
use std::{path::PathBuf, process::Command};
use crate::trace_tailwind::paths::{
get_tailwind_binary_path, get_tailwind_classes_dir, get_tailwind_classes_file_exists,
};
use crate::trace_tailwind::validate::validate_tailwind_classes;
pub(crate) fn bundle_tailwind() -> TokenStream2 {
if std::env::var("VERTIGO_BUNDLE").is_ok() && get_tailwind_classes_file_exists() {
let bundle = match run_external_tailwind() {
Ok(output) => output,
Err(err) => {
emit_error!(Span::call_site(), "Tailwind: {}", err);
return quote! {};
}
};
if let Err(err) = validate_tailwind_classes(&bundle) {
emit_error!(
proc_macro::Span::call_site(),
"Failed to validate Tailwind classes: {}",
err
);
}
quote! {
vertigo::get_driver().register_bundle(#bundle);
}
} else {
quote! {}
}
}
fn run_external_tailwind() -> Result<String, Box<dyn Error>> {
let tailwind_dir = get_tailwind_classes_dir()?;
let input_styles_path = tailwind_dir.join("input.css");
let mut input_styles_file = std::fs::OpenOptions::new()
.create(true)
.truncate(true)
.write(true)
.open(input_styles_path)
.map_err(|err| format!("Unable to create input.css file: {}", err))?;
let user_tw_input_path = PathBuf::from(Span::call_site().file())
.parent()
.ok_or("Unable to get parent directory")?
.join("tailwind.css");
if let Ok(user_defined_input) = read_to_string(&user_tw_input_path) {
write!(input_styles_file, "{user_defined_input}")
.map_err(|err| format!("Unable to write custom tailwind input to file: {}", err))?;
} else {
writeln!(input_styles_file, "@import \"tailwindcss\";")
.map_err(|err| format!("Unable to default tailwind input to file: {}", err))?;
}
let tailwind_executable = get_tailwind_binary_path()?;
let tailwind_executable_abs = tailwind_executable
.canonicalize()
.unwrap_or(tailwind_executable);
let command = match Command::new(&tailwind_executable_abs)
.args(["-i", "input.css"])
.current_dir(&tailwind_dir)
.output()
{
Ok(cmd) => cmd,
Err(err) => {
abort_call_site!(
"Failed to run external tailwind: {}", err;
help = "Path was: {}", tailwind_executable_abs.display()
);
}
};
let output = String::from_utf8_lossy(&command.stdout);
if command.status.success() {
Ok(output.into_owned())
} else {
let err_output = String::from_utf8_lossy(&command.stderr);
abort_call_site!(
"Tailwind run failed: {}", err_output;
note = "Tailwind output: {}", output;
);
}
}