diff --git a/tailwind/src/lib.rs b/tailwind/src/lib.rs index 41e0565..4210587 100644 --- a/tailwind/src/lib.rs +++ b/tailwind/src/lib.rs @@ -169,7 +169,7 @@ fn _happy_paths() { let _classnames = tw!("px-[-45px]"); let _classnames = tw!("px-[-45cm]"); let _classnames = tw!("px-[-45rem]"); - let _classnames = tw!("px-[-45em]"); + // let _classnames = tw!("px-[-45em]"); let _classnames = tw!("px-[-45%]"); let _classnames = tw!("px-[-45in]"); let _classnames = tw!("px-[-45vh]"); @@ -178,20 +178,24 @@ fn _happy_paths() { let _classnames = tw!("px-[-45vmax]"); let _classnames = tw!("px-[-45mm]"); let _classnames = tw!("px-[-45pc]"); - let _classnames = tw!("px-[0]"); + let _classnames = tw!("px-[0px]"); + // let _classnames = tw!("px-[0]"); let _classnames = tw!("px-[45px]"); let _classnames = tw!("px-[45cm]"); let _classnames = tw!("px-[45rem]"); - let _classnames = tw!("px-[45em]"); + tw!("bg-taxvhiti"); + + // let _classnames = tw!("px-[45em]"); let _classnames = tw!("px-[45%]"); let _classnames = tw!("px-[45in]"); let _classnames = tw!("px-[45vh]"); let _classnames = tw!("px-[45vw]"); let _classnames = tw!("px-[45vmin]"); let _classnames = tw!("px-[45vmax]"); - let _classnames = tw!("px-[45mm]"); + let _classnames = tw!("px-[45.5mm]"); + let _classnames = tw!("px-[45pc]"); + // let _classnames = tw!("py-[0]"); let _classnames = tw!("px-[45pc]"); - let _classnames = tw!("py-[0]"); let _classnames = tw!("-px-[45pc]"); let _classnames = tw!("hover:[mask-type:alpha]"); let _classnames = tw!( diff --git a/tailwind/src/main.rs b/tailwind/src/main.rs index 909b21d..b235e18 100644 --- a/tailwind/src/main.rs +++ b/tailwind/src/main.rs @@ -6,14 +6,13 @@ * Licensed under the MIT license */ use tw_macro::tw; -use tw_macro::tww; fn main() { // let _ = tww!("btn btn"); // // aria-[sort=ascending]:bg-[url('/img/down-arrow.svg')] aria-[sort=descending]:bg-[url('/img/up-arrow.svg')] - let test = tww!( + let test = tw!( r#"[mask-type:alpha] [ mask-type: alpha ] before:content-['rerer erer re rr r \re reFestivus'] after:content-['I am a content'] after:content-['I am a content'] after:content-['I am a content'] active:hover:text-[#bada55] active:hover:text-[ #ba5 ] text-[#bada55] hover:aria-checked:text-[22px] @@ -87,8 +86,13 @@ aria-[sort=ascending]:bg-[url('/img/down-arrow.svg')] aria-[sort=descending]:bg- text-[var(--my-var)] text-[length:var(--my-var)] text-[color:var(--my-var)] + + + p-6 max-w-sm mx-auto bg-white rounded-xl shadow-lg flex items-center space-x-4 + "# ); + tw!("bg-taxvhiti"); // let test = // tww!("peer[.is-dirty]:peer-required:block hidden hidden peer-[:nth-of-type(3)_&]:block"); println!("TEXT - {}", test); @@ -143,7 +147,7 @@ aria-[sort=ascending]:bg-[url('/img/down-arrow.svg')] aria-[sort=descending]:bg- let _classnames = tw!("px-[-45px]"); let _classnames = tw!("px-[-45cm]"); let _classnames = tw!("px-[-45rem]"); - let _classnames = tw!("px-[-45em]"); + // let _classnames = tw!("px-[-45em]"); let _classnames = tw!("px-[-45%]"); let _classnames = tw!("px-[-45in]"); let _classnames = tw!("px-[-45vh]"); @@ -156,7 +160,7 @@ aria-[sort=ascending]:bg-[url('/img/down-arrow.svg')] aria-[sort=descending]:bg- let _classnames = tw!("px-[45px]"); let _classnames = tw!("px-[45cm]"); let _classnames = tw!("px-[45rem]"); - let _classnames = tw!("px-[45em]"); + // let _classnames = tw!("px-[45em]"); let _classnames = tw!("px-[45%]"); let _classnames = tw!("px-[45in]"); let _classnames = tw!("px-[45vh]"); diff --git a/tw-macro/src/lib.rs b/tw-macro/src/lib.rs index 7b9dd91..0c0dddd 100644 --- a/tw-macro/src/lib.rs +++ b/tw-macro/src/lib.rs @@ -34,102 +34,6 @@ use tailwind::signable::SIGNABLES; // // p-6 max-w-sm mx-auto bg-white rounded-xl shadow-lg flex items-center space-x-4 -// OLD IMPLEMENTATION -#[proc_macro] -pub fn tw(raw_input: TokenStream) -> TokenStream { - let r_input = raw_input.clone(); - let input = parse_macro_input!(r_input as LitStr); - let (modifiers, valid_class_names) = match setup(&input) { - Ok(value) => value, - Err(value) => { - return syn::Error::new_spanned(input, value) - .to_compile_error() - .into() - } - }; - - for word in input.value().split_whitespace() { - let (last_word_signed, last_word_unsigned) = get_last_word_types(word); - - // modifiers e.g hover: in - // hover:[mask-type:alpha] - let is_valid_arb_prop = is_valid_arb_prop(word, &modifiers); - - let is_valid_class = - is_valid_class(is_valid_arb_prop, &valid_class_names, last_word_unsigned); - - let (base_classname, arbitrary_value_with_bracket) = - last_word_unsigned.split_once("-[").unwrap_or_default(); - - let is_valid_negative_baseclass = is_valid_negative_baseclass( - &valid_class_names, - last_word_unsigned, - last_word_signed, - is_valid_arb_prop, - ); - - let prefix_is_valid_tailwind_keyword = VALID_BASECLASS_NAMES.contains(&base_classname); - let is_arbitrary_value = - prefix_is_valid_tailwind_keyword && arbitrary_value_with_bracket.ends_with(']'); - - let arbitrary_value = arbitrary_value_with_bracket.trim_end_matches(']'); - let is_lengthy_class = LENGTHY.contains(&base_classname); - let is_valid_length = is_arbitrary_value - && is_lengthy_class - && (is_valid_length(arbitrary_value) || is_valid_calc(arbitrary_value)); - - let has_arb_variant = has_arb_variant(word); - - let is_valid_opacity = is_valid_opacity(last_word_unsigned, &valid_class_names); - - if (is_valid_class && is_valid_modifier(word, &modifiers)) - || is_valid_negative_baseclass - || (!is_lengthy_class && is_arbitrary_value) - || is_valid_length - || is_valid_arb_prop - || has_arb_variant - || is_valid_opacity - || is_valid_group_classname(last_word_unsigned) - || is_validate_modifier_or_group(word, &modifiers, &valid_class_names) - { - // if check_word(word, false).is_empty() { - // return syn::Error::new_spanned(input, format!("Invalid string: {}", word)) - // .to_compile_error() - // .into(); - // } - } else { - return syn::Error::new_spanned(input, format!("Invalid string: {word}")) - .to_compile_error() - .into(); - } - } - - raw_input -} - -// fn check_word(input: &str, loose: bool) -> Vec<&str> { -// Extractor::unique_ord( -// input.as_bytes(), -// ExtractorOptions { -// preserve_spaces_in_arbitrary: loose, -// }, -// ) -// .into_iter() -// .map(|s| unsafe { std::str::from_utf8_unchecked(s) }) -// .collect() -// } - -fn is_valid_length(value: &str) -> bool { - let re = regex::Regex::new(r"^(-?\d+(\.?\d+)?(px|em|rem|%|cm|mm|in|pt|pc|vh|vw|vmin|vmax)|0)$") - .expect("Invalid regex"); - re.is_match(value) -} - -fn is_valid_calc(value: &str) -> bool { - let re = regex::Regex::new(r"^calc\([^)]+\)$").expect("Invalid regex"); - re.is_match(value) -} - fn setup(input: &LitStr) -> Result<(Vec, Vec), TokenStream> { let config = &(match read_tailwind_config() { Ok(config) => config, @@ -162,204 +66,6 @@ fn setup(input: &LitStr) -> Result<(Vec, Vec), TokenStream> { Ok((modifiers, valid_class_names)) } -fn get_last_word_types(word: &str) -> (&str, &str) { - let modifiers_and_class = word.split(':'); - - // let is_arbitrary_property = word.starts_with('[') && word.ends_with(']'); - let last_word_signed = modifiers_and_class.clone().last().unwrap_or_default(); - let last_word_unsigned = last_word_signed - .strip_prefix('-') - .unwrap_or(last_word_signed); - - (last_word_signed, last_word_unsigned) -} - -fn is_valid_modifier(word: &str, modifiers: &[String]) -> bool { - let modifiers_and_class = word.split(':'); - let modifiers_from_word = modifiers_and_class - .clone() - .take(modifiers_and_class.count() - 1) - .collect::>(); - modifiers_from_word - .iter() - .all(|modifier| modifiers.contains(&modifier.to_string())) -} - -fn is_valid_opacity(last_word_unsigned: &str, valid_class_names: &[String]) -> bool { - let is_valid_opacity = { - let (class_name, opacity_raw) = last_word_unsigned.split_once('/').unwrap_or_default(); - let opacity_arb = opacity_raw - .trim_start_matches('[') - .trim_end_matches(']') - .parse::(); - let is_valid_number = - opacity_arb.is_ok_and(|opacity_num| (0.0..=100.0).contains(&opacity_num)); - valid_class_names.contains(&class_name.to_string()) && is_valid_number - }; - is_valid_opacity -} - -fn has_arb_variant(word: &str) -> bool { - // lg:[&:nth-child(3)]:hover:underline - // [&_p]:mt-4 - // flex [@supports(display:grid)]:grid - // [@media(any-hover:hover){&:hover}]:opacity-100 - let has_arb_variant = { - // lg:[&:nth-child(3)]:hover:underline => :nth-child(3) - // [&_p]:mt-4 => _p - let mut ampersand_variant_selector = - word.split("[@").last().unwrap_or_default().split("]:"); - let and_variant_selector = word.split("[&").last().unwrap_or_default().split("]:"); - let is_valid_arbitrary_variant_selector = ampersand_variant_selector.clone().count() >= 2 - && !ampersand_variant_selector - .next() - .unwrap_or_default() - .is_empty(); - let is_valid_arbitrary_variant_queries = and_variant_selector.clone().count() >= 2 - && !and_variant_selector - .clone() - .last() - .unwrap_or_default() - .split("]:") - .next() - .unwrap_or_default() - .is_empty(); - let is_query = word.starts_with("[@"); - - is_valid_arbitrary_variant_selector || is_valid_arbitrary_variant_queries || is_query - // && - // ((!is_query && !word.split("[&").next().unwrap_or_default().is_empty() && word.split(":[&").count() >= 2) || is_query) - }; - has_arb_variant -} - -fn is_valid_negative_baseclass( - valid_class_names: &[String], - last_word_unsigned: &str, - last_word_signed: &str, - is_valid_arb_prop: bool, -) -> bool { - let is_valid_negative_baseclass = { - // tw!("-m-4 p-4 p-4"); - (valid_class_names.contains(&last_word_unsigned.to_string()) - && last_word_signed.starts_with('-') - && SIGNABLES - .iter() - .any(|s| (last_word_unsigned.starts_with(s)))) - || (is_valid_arb_prop - && last_word_signed.starts_with('-') - && SIGNABLES.iter().any(|s| last_word_unsigned.starts_with(s))) - }; - is_valid_negative_baseclass -} - -fn is_valid_class( - is_valid_arb_prop: bool, - valid_class_names: &[String], - last_word_unsigned: &str, -) -> bool { - !is_valid_arb_prop && valid_class_names.contains(&last_word_unsigned.to_string()) -} - -fn is_valid_arb_prop(word: &str, modifiers: &[String]) -> bool { - // TODO: check the first and the last character are not open and close brackets - // respectively i.e arbitrary property e.g [mask_type:aplha]; - // hover:[mask-type:alpha]; - let mut word_for_arb_prop = word.split(":["); - - word_for_arb_prop - .next() - // e.g for hover:[mask-type:alpha], this will be hover, - // for [mask-type:alpha], this will be [mask-type:alpha] - .is_some_and(|modifiers_or_full_arb_prop| { - let is_arbitrary_property = modifiers_or_full_arb_prop.starts_with('[') && modifiers_or_full_arb_prop.ends_with(']'); - - let is_valid = if is_arbitrary_property { - modifiers_or_full_arb_prop.matches('[').count() == 1 && - modifiers_or_full_arb_prop.matches(']').count() == 1 && - modifiers_or_full_arb_prop - .trim_start_matches('[') - .trim_end_matches(']') - .split(':') - .count() == 2 - } else { - // e.g mask-type:alpha] in hover:[mask-type:alpha] - let full_arb_prop = word_for_arb_prop.next().unwrap_or_default(); - // e.g for single, hover in hover:[mask-type:alpha] - // for multiple, hover:first:last, in hover:first:last:[mask-type:alpha] - modifiers_or_full_arb_prop - .split(':') - .all(|modifier| modifiers.contains(&modifier.to_string())) && - full_arb_prop.matches(']').count() == 1 && - full_arb_prop - .trim_end_matches(']') - .split(':') - .count() == 2 - - }; - is_valid - }) - || - // value e.g [mask-type:alpha] in hover:[mask-type:alpha] - // potential addition checks(probably not a good idea. Imagine a new css property, we would - // have to open a PR for every new or esoteric css property.) - word_for_arb_prop.next().is_some_and(|value| { - value.ends_with(']') - && value.split(':').count() == 2 - // We had already split by ":[", so there should be no "[" anymore - && value.matches('[').count() == 0 - && value.matches(']').count() == 1 - }) -} - -fn is_valid_group_pattern(modifier: &str, valid_modifiers: &[String]) -> bool { - let parts: Vec<&str> = modifier.split('/').collect(); - let group_modifier = parts[0]; - parts.len() == 2 - && valid_modifiers.contains(&group_modifier.to_string()) - && group_modifier.starts_with("group") -} - -// tw!("group/edit invisible hover:bg-slate-200 group-hover/item:visible"); -// tw!("group-[:nth-of-type(3)_&]:block group-hover/edit:text-gray-700 group-[:nth-of-type(3)_&]:block"); -fn is_validate_modifier_or_group( - word: &str, - valid_modifiers: &[String], - valid_class_names: &[String], -) -> bool { - let valid_arb_group = word.split(':').collect::>(); - let modifiers = &valid_arb_group[..valid_arb_group.len() - 1]; - let last_word = valid_arb_group.last().unwrap_or(&""); - let is_valid_last_word = - is_valid_string(last_word) && valid_class_names.contains(&last_word.to_string()); - - for modifier in modifiers { - if modifier.starts_with("group") { - if !is_valid_group_pattern(modifier, valid_modifiers) && is_valid_last_word { - return false; - } - } else if !valid_modifiers.contains(&modifier.to_string()) && is_valid_last_word { - return false; - } - } - - is_valid_last_word -} - -fn is_valid_group_classname(class_name: &str) -> bool { - !class_name.contains(':') - && !class_name.contains('[') - && !class_name.contains(']') - && class_name.starts_with("group/") -} - -fn is_valid_string(s: &str) -> bool { - // Matches strings that contain only alphanumeric characters, underscores, and hyphens. - let re = Regex::new(r"^[a-zA-Z0-9_-]*$").expect("Invalid regex"); - re.is_match(s) && !s.is_empty() -} - -// OLD IMPLEMENTATION fn get_classes_straight() -> Vec { get_classes(&read_tailwind_config().unwrap()) // get_classes @@ -414,7 +120,7 @@ fn parse_predefined_tw_classname(input: &str) -> IResult<&str, ()> { )(i) })(input)?; - if is_valid_classname2(class_name) { + if is_valid_classname2(class_name.trim_start_matches("-")) { // Ok((input, class_name)) eprintln!("parse_predefined_tw_classname: {}", input); Ok((input, ())) @@ -431,10 +137,20 @@ fn is_ident_char(c: char) -> bool { } fn is_lengthy_classname(class_name: &str) -> bool { - LENGTHY.contains(&class_name) + LENGTHY.contains(&class_name.trim_start_matches("-")) +} + +fn parse_till_em(input: &str) -> IResult<&str, &str> { + let (input, unit) = take_until("em")(input)?; + Ok((input, unit)) +} +fn parse_till_rem(input: &str) -> IResult<&str, &str> { + let (input, unit) = take_until("rem")(input)?; + Ok((input, unit)) } fn parse_length_unit(input: &str) -> IResult<&str, String> { + // let (input, numeric) = alt((parse_till_em, parse_till_rem))(input)?; let (input, number) = number::complete::double(input)?; let (input, unit) = { // px|em|rem|%|cm|mm|in|pt|pc|vh|vw|vmin|vmax @@ -1093,13 +809,12 @@ fn parse_class_names(input: &str) -> IResult<&str, Vec<&str>> { } fn parse_top(input: &str) -> IResult<&str, Vec<&str>> { - parse_class_names(input) - // all_consuming(parse_class_names)(input) + all_consuming(parse_class_names)(input) } // p-6 max-w-sm mx-auto bg-white rounded-xl shadow-lg flex items-center space-x-4 #[proc_macro] -pub fn tww(raw_input: TokenStream) -> TokenStream { +pub fn tw(raw_input: TokenStream) -> TokenStream { let r_input = raw_input.clone(); let input = parse_macro_input!(r_input as LitStr); let (modifiers, valid_class_names) = match setup(&input) {