diff --git a/Cargo.toml b/Cargo.toml index c55f5d8..5dc8275 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,11 +37,10 @@ keywords = [ [workspace.dependencies] twust = { path = "twust" } twust_macro = { path = "twust_macro" } -# tailwind = { path = "tailwind" } - proc-macro2 = "1.0.69" quote = "1.0.33" -syn = "2.0.38" +syn = { version = "2.0.38" } +# syn = { version = "2.0.38", features = ["full"] } nom = "7.1.3" static_assertions = "1.1.0" serde = { version = "1.0.188", features = ["derive"] } diff --git a/tailwind/Cargo.toml b/tailwind/Cargo.toml index 2328c5c..20bc4d1 100644 --- a/tailwind/Cargo.toml +++ b/tailwind/Cargo.toml @@ -11,6 +11,5 @@ documentation.workspace = true [dependencies] twust = { workspace = true, features = ["daisyui"] } - serde = "1.0.188" serde_json = "1.0.105" diff --git a/twust/src/lib.rs b/twust/src/lib.rs new file mode 100644 index 0000000..8d757ac --- /dev/null +++ b/twust/src/lib.rs @@ -0,0 +1,75 @@ +/* + * Author: Oyelowo Oyedayo + * Email: oyelowo.oss@gmail.com + * Copyright (c) 2025 Oyelowo Oyedayo + * Licensed under the MIT license + */ + +pub use twust_macro::tw as twust; +// // use twust_macro::xtw; +// +// #[macro_export] +// macro_rules! tw { +// // Case: Single string +// ($single:literal) => { +// $single +// }; +// ($($class:literal),*) => { +// concat!($($class, " "),*) +// }; +// +// ([$($class:expr),*]) => { +// concat!($($class, " "),*) +// }; +// } +// +// fn main() { +// let _classnames_single = tw!("scroll-m-14 flex supports-grid:grid supports-[display:grid]:grid"); +// let _classnames_array = tw!["scroll-m-14", "flex", "supports-grid:grid", "supports-[display:grid]:grid"]; +// let _classnames_array2 = tw!(["scroll-m-14", "flex", "supports-grid:grid", "supports-[display:grid]:grid"]); +// // let _classnames_array2 = twx!(["scroll-m-14", "flex", "supports-grid:grid", "supports-[display:grid]:grid"]); +// // let _classnames_array2 = twx!(["scroll-m-14", "flex", "supports-grid:grid", "supports-[display:grid]:grid"]); +// } +// + + + +/// Typechecks tailwindcss classnames at compile time. +/// +/// ## Features: +/// - Supports **single string** literals (`tw!("class1 class2")`) +/// - Supports **multiple string arguments** (`tw!["class1", "class2"]`) +/// - Supports **arrays of strings** (`tw!(["class1", "class2"])`) +/// +/// +/// ## Example Usage +/// +/// ```rust, ignore +/// use twust::tw; +/// +/// let single_class = tw!("scroll-m-14 flex supports-grid:grid supports-[display:grid]:grid"); +/// let multiple_classes = tw!["scroll-m-14", "flex", "supports-grid:grid", "supports-[display:grid]:grid"]; +/// let array_classes = tw!(["scroll-m-14", "flex", "supports-grid:grid", "supports-[display:grid]:grid"]); +/// +/// assert_eq!(single_class, "scroll-m-14 flex supports-grid:grid supports-[display:grid]:grid"); +/// assert_eq!(multiple_classes, "scroll-m-14 flex supports-grid:grid supports-[display:grid]:grid"); +/// assert_eq!(array_classes, "scroll-m-14 flex supports-grid:grid supports-[display:grid]:grid"); +/// ``` +/// +/// ## Notes +/// - The macro supports **both** `tw!(...)` and `tw![...]` syntax. +/// - It ensures at compile time that only string literals are used for class names. +#[macro_export] +macro_rules! tw { + ($single:literal) => { + $single + }; + + ($($class:literal),*) => { + concat!($($class, " "),*) + }; + + ([$($class:expr),*]) => { + concat!($($class, " "),*) + }; +} diff --git a/twust_macro/Cargo.toml b/twust_macro/Cargo.toml index 044e82e..c3c44b0 100644 --- a/twust_macro/Cargo.toml +++ b/twust_macro/Cargo.toml @@ -15,8 +15,6 @@ keywords = { workspace = true } [features] daisyui = [] -# workspace = "." - [dependencies] syn = { workspace = true } proc-macro2 = { workspace = true } @@ -26,7 +24,6 @@ static_assertions = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } regex = { workspace = true } -# tailwindcss-core = { workspace = true } [lib] diff --git a/twust_macro/src/lib.rs b/twust_macro/src/lib.rs index cd9cb5d..075a774 100644 --- a/twust_macro/src/lib.rs +++ b/twust_macro/src/lib.rs @@ -5,8 +5,6 @@ * Licensed under the MIT license */ -use std::collections::HashSet; - use nom::{ branch::alt, bytes::complete::{tag, take_until, take_while1}, @@ -17,6 +15,7 @@ use nom::{ sequence::{preceded, tuple}, IResult, }; +use std::collections::HashSet; use syn::{parse_macro_input, LitStr}; mod config; mod plugins; @@ -29,7 +28,6 @@ use tailwind::{ use config::{get_classes, noconfig::UNCONFIGURABLE, read_tailwind_config}; use proc_macro::TokenStream; use tailwind::signable::SIGNABLES; -// use tailwindcss_core::parser::{Extractor, ExtractorOptions}; fn setup(input: &LitStr) -> Result<(Vec, Vec), TokenStream> { let config = &(match read_tailwind_config() { @@ -766,3 +764,40 @@ pub fn tw(raw_input: TokenStream) -> TokenStream { } .into() } + +// // Requires featues = full. Dont need it, can just use macrorules +// #[proc_macro] +// pub fn tws(raw_input: TokenStream) -> TokenStream { +// let input = parse_macro_input!(raw_input as Expr); +// +// let mut all_classnames = Vec::new(); +// +// match input { +// Expr::Array(array) => { +// for expr in array.elems.iter() { +// if let Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(lit_str), .. }) = expr { +// all_classnames.push(lit_str.value()); +// } else { +// return syn::Error::new_spanned(expr, "Expected string literals in the array") +// .to_compile_error() +// .into(); +// } +// } +// } +// Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(lit_str), .. }) => { +// all_classnames.push(lit_str.value()); +// } +// _ => { +// return syn::Error::new_spanned(input, "Expected a string literal or an array of string literals") +// .to_compile_error() +// .into(); +// } +// } +// +// let concatenated = all_classnames.join(" "); +// +// quote::quote! { +// #concatenated +// } +// .into() +// }