use proc_macro::TokenStream;
use proc_macro2::{Span, TokenTree};
use quote::{format_ident, quote, quote_spanned};
use rcss_core::{self, macro_helper::macro_input, CssEmbeding, CssOutput};
use syn::spanned::Spanned;
#[cfg(any(
all(feature = "lightningcss", feature = "postcss"),
all(feature = "lightningcss", feature = "stylers"),
all(feature = "postcss", feature = "stylers")
))]
compile_error!("Can't use more than one css processor at the same time");
mod fallback_ide;
#[proc_macro]
pub fn css_module(tokens: TokenStream) -> TokenStream {
let v = css_inner(false, CssEmbeding::CssModules)
.unwrap_or_else(|| fallback_ide::parse(tokens.into()));
v.generate_css_module(None).into()
}
#[proc_macro]
pub fn css_module_inline(tokens: TokenStream) -> TokenStream {
let v = css_inner(false, CssEmbeding::CssModules)
.unwrap_or_else(|| fallback_ide::parse(tokens.into()));
let module = v.generate_css_module(None);
let style = v.style_string();
let is_proc_macro = proc_macro::is_available();
let envs = std::env::vars()
.map(|(k, v)| format!("{}={}", k, v))
.collect::<Vec<_>>()
.join("\n");
let debug = format!("is_proc_macro = {}, envs = {}", is_proc_macro, envs);
quote! {
({let _ = #debug;
#module
},
#style)
}
.into()
}
#[proc_macro]
pub fn css_scoped(tokens: TokenStream) -> TokenStream {
let v =
css_inner(false, CssEmbeding::Scoped).unwrap_or_else(|| fallback_ide::parse(tokens.into()));
let class_name = v.class_name();
quote! {
#class_name
}
.into()
}
#[proc_macro]
pub fn css_scoped_inline(_tokens: TokenStream) -> TokenStream {
if let Some(v) = css_inner(false, CssEmbeding::Scoped) {
let class_name = v.class_name();
let style = v.style_string();
return quote! {
(
#class_name,
#style
)
}
.into();
}
quote! {}.into()
}
#[proc_macro]
pub fn css_module_struct(tokens: TokenStream) -> TokenStream {
let tokens: proc_macro2::TokenStream = tokens.into();
let mut token_iter = tokens.into_iter();
let Some(TokenTree::Ident(ident)) = token_iter.next() else {
return quote! {
compile_error!("Expected struct name")
}
.into();
};
let eq = token_iter.next(); let gt = token_iter.next(); if !matches!(eq, Some(TokenTree::Punct(p)) if p.as_char() == '=') {
return quote! {
compile_error!("Expected =>")
}
.into();
}
if !matches!(gt, Some(TokenTree::Punct(p)) if p.as_char() == '>') {
return quote! {
compile_error!("Expected =>")
}
.into();
}
let v = css_inner(true, CssEmbeding::CssModules)
.unwrap_or_else(|| fallback_ide::parse(token_iter.collect()));
v.generate_css_module(Some(ident)).into()
}
#[proc_macro]
pub fn css_module_mod(tokens: TokenStream) -> TokenStream {
let tokens: proc_macro2::TokenStream = tokens.into();
let mut token_iter = tokens.into_iter();
let ident = token_iter.next();
let Some(TokenTree::Ident(mod_name)) = ident else {
return quote_spanned! {
ident.span() =>
compile_error!("Expected struct name")
}
.into();
};
let eq = token_iter.next(); let gt = token_iter.next(); if !matches!(&eq, Some(TokenTree::Punct(p)) if p.as_char() == '=') {
return quote_spanned! {
eq.span()=>
compile_error!("Expected =>")
}
.into();
}
if !matches!(>, Some(TokenTree::Punct(p)) if p.as_char() == '>') {
return quote_spanned! {
gt.span() =>
compile_error!("Expected =>")
}
.into();
}
let v = css_inner(true, CssEmbeding::CssModules)
.unwrap_or_else(|| fallback_ide::parse(token_iter.collect()));
let struct_generated = v.generate_css_module(Some(format_ident!("Css")));
let style = v.style_string();
let stream = quote! {
mod #mod_name {
#struct_generated
static STYLE: &'static str = #style;
}
};
stream.into()
}
fn css_inner(struct_ident_expected: bool, embeding: CssEmbeding) -> Option<CssOutput> {
let text = Span::call_site().source_text()?;
let text = macro_input(&text, struct_ident_expected)?;
let mut processor =
rcss_core::CssProcessor::new(rcss_core::CssPreprocessor::LightningCss, embeding);
let output = processor.process_style(&text);
Some(output)
}