Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

5 Improve Robustness of Tailwind Class Parsing in Rust #6

Merged
merged 46 commits into from
Oct 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
21b3134
Base implementation of new classname parser
Oyelowo Oct 7, 2023
a31122c
SUpport kv arbitrary property
Oyelowo Oct 8, 2023
4b466f0
Support lengthy arbitrary classname
Oyelowo Oct 8, 2023
0582cf6
Add colorful classnames
Oyelowo Oct 9, 2023
9e0e2d4
Validate arbitrary hex color
Oyelowo Oct 9, 2023
0964587
Allow sacing in arbitrary length and color
Oyelowo Oct 9, 2023
496ce9a
Support arbitrary content
Oyelowo Oct 9, 2023
59b83d6
Support prefefined and arbitrary opacity
Oyelowo Oct 10, 2023
e89ef69
Support url
Oyelowo Oct 10, 2023
af1f4f6
Support arbitrary css value
Oyelowo Oct 10, 2023
8a57273
Support arbitrary css variable
Oyelowo Oct 10, 2023
408c97d
Support arbitrary css variable 2 text-[var(--my-var)]
Oyelowo Oct 10, 2023
e35cdc1
Support css value variable ie text-[length:var(--my-var)]
Oyelowo Oct 10, 2023
b0a4214
Refactor css value parser
Oyelowo Oct 10, 2023
1d31915
Support arbitrary modiifer front and back selector
Oyelowo Oct 10, 2023
219a918
Support aribitrary @supports modifier e.g [@supports(display:grid)]:grid
Oyelowo Oct 10, 2023
439d591
Supports arbitrary @media modifier
Oyelowo Oct 10, 2023
610dddc
Support group and peer modifier
Oyelowo Oct 10, 2023
e1581ba
Support group and peer modifier and selector
Oyelowo Oct 10, 2023
3c35d65
Support arbitrary rgb and rbga colors
Oyelowo Oct 10, 2023
5c2053e
Support supports arbitrary
Oyelowo Oct 10, 2023
2766715
Support aria arbitrary
Oyelowo Oct 10, 2023
3cbfed2
Support group-aria
Oyelowo Oct 10, 2023
f6cdcc8
Support data arbitrary
Oyelowo Oct 10, 2023
4c29468
Support min max simple arbitrary mediaqueries
Oyelowo Oct 10, 2023
00b1830
Complete modifier classnames support
Oyelowo Oct 10, 2023
706a386
Switch to new tw macro parser
Oyelowo Oct 10, 2023
2868f6e
Parsing leading and ending spaces
Oyelowo Oct 10, 2023
f32610c
Parse arbitrary unitless metric
Oyelowo Oct 10, 2023
f32d5a5
Allow unitless metric values
Oyelowo Oct 10, 2023
10dc111
Implement custom parser for float and number to disambiguate scientif…
Oyelowo Oct 10, 2023
ec2a2df
Improve disambiguation
Oyelowo Oct 10, 2023
8ddf8b4
Cleanup parsers
Oyelowo Oct 10, 2023
836d638
Cleanup debug printing
Oyelowo Oct 10, 2023
7f13a25
Handle signable values better
Oyelowo Oct 10, 2023
3465f57
Take into consideration if classname is signable before allowing signs
Oyelowo Oct 11, 2023
c7fac02
Allow unitless value in test
Oyelowo Oct 11, 2023
288c29b
Allow underscore for rgb and rbga
Oyelowo Oct 11, 2023
c1754a7
Disallow spacing within rgba and rbga
Oyelowo Oct 11, 2023
822b81c
Support arbitrary contetnt with arrows
Oyelowo Oct 11, 2023
5294497
Disallow spacing within each modifier_classnames
Oyelowo Oct 11, 2023
3012b03
Support more character types in arbitrary kv values
Oyelowo Oct 11, 2023
5f0afba
Add more examples
Oyelowo Oct 11, 2023
9d296a1
Fix and format
Oyelowo Oct 11, 2023
d63540f
Use hashset for classses for 0(1) class/modifier names lookups
Oyelowo Oct 11, 2023
6039a52
Rename crate to from tailwind-rust to twust
Oyelowo Oct 11, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,13 @@ documentation = "https://codebreather.com/oyelowo"


[workspace.dependencies]
tw-macro = { path = "tw-macro" }
twust = { path = "tw-macro" }
# tailwind = { path = "tailwind" }

proc-macro2 = "1.0.66"
quote = "1.0.33"
syn = "2.0.29"
nom = "7.1.3"
static_assertions = "1.1.0"
serde = { version = "1.0.188", features = ["derive"] }
serde_json = "1.0.105"
Expand Down
38 changes: 19 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# `tw-macro`
# `twust`

A powerful Rust macro to validate TailwindCSS class names at compile-time.
Twust is a powerful static checker in rust for TailwindCSS class names at
compile-time.

<!--
<!--
<img width="1048" alt="Screenshot 2023-09-09 at 19 51 17" src="https://github.com/Oyelowo/tailwind-rust/assets/31687368/14c79cae-b7f5-4ea2-b42f-5b54435b5e08">
<img width="497" alt="Screenshot 2023-09-09 at 19 50 59" src="https://github.com/Oyelowo/tailwind-rust/assets/31687368/69fcf619-7f12-4e23-8a78-2d9f8fe83b4d">
-->
<img width="1490" alt="Screenshot 2023-09-09 at 19 51 09" src="https://github.com/Oyelowo/tailwind-rust/assets/31687368/9c9dd377-a696-42d1-bd69-251e76064b53">

<img width="1490" alt="Screenshot 2023-09-09 at 19 51 09" src="https://github.com/Oyelowo/tailwind-rust/assets/31687368/9c9dd377-a696-42d1-bd69-251e76064b53">

## Table of Contents

Expand All @@ -25,18 +25,18 @@ A powerful Rust macro to validate TailwindCSS class names at compile-time.

## Overview

`tw-macro` is a Rust procedural macro that provides compile-time validation for
TailwindCSS class names. Leveraging the power of Rust's macro system, `tw-macro`
`twust` is a Rust procedural macro that provides compile-time validation for
TailwindCSS class names. Leveraging the power of Rust's macro system, `twust`
ensures that you only use valid TailwindCSS class names, preventing runtime
errors and promoting a more robust development experience.

## Installation

Add `tw-macro` to your `Cargo.toml`:
Add `twust` to your `Cargo.toml`:

```toml
[dependencies]
tw-macro = "0.1.0"
twust = "0.1.0"
```

## Usage
Expand Down Expand Up @@ -92,24 +92,24 @@ applications. However, its flexibility can also lead to potential pitfalls:

## Solution

`tw-macro` addresses these challenges by offering:
`twust` addresses these challenges by offering:

- **Compile-time Validation:** By checking the validity of TailwindCSS class
names at compile time, `tw-macro` prevents invalid class names from making
names at compile time, `twust` prevents invalid class names from making
their way into the production code.

- **Seamless Integration:** As a Rust macro, `tw-macro` integrates seamlessly
- **Seamless Integration:** As a Rust macro, `twust` integrates seamlessly
into your Rust workflow, offering immediate feedback without the need for
external tools or manual validation.

- **Plugin Support:** With tw-macro, you can easily integrate popular plugins
- **Plugin Support:** With twust, you can easily integrate popular plugins
like daisyui by merely specifying them as a feature, ensuring a consistent and
extended development experience.
- **Effortless Code Reusability:** The ability to copy-paste and reuse your
TailwindCSS code without any manual mappings or transformations. Just wrap
your code with the macro, and you're set.

- **Optimized Builds:** By ensuring only valid class names are used, `tw-macro`
- **Optimized Builds:** By ensuring only valid class names are used, `twust`
helps in reducing the unnecessary bloat in the final CSS bundle.

## Features
Expand Down Expand Up @@ -168,16 +168,16 @@ nonexistent classes and code completion for available classes.
adds an external dependency, which might not be suitable for all projects,
especially those that want to minimize their dependency tree.

### Approach with `tw-macro`
### Approach with `twust`

Our solution with `tw-macro` offers a more streamlined and integrated approach:
Our solution with `twust` offers a more streamlined and integrated approach:

- **Simpler Setup:** Just add the macro to your project and start using it. No
need for external tools or additional configuration steps.
- **Real-time Validation:** Instead of generating static Rust code from
TailwindCSS, `tw-macro` validates class names in real-time during the
TailwindCSS, `twust` validates class names in real-time during the
compilation process.
- **No External Dependencies:** `tw-macro` is self-contained, meaning you don't
- **No External Dependencies:** `twust` is self-contained, meaning you don't
need any external tools like the `tailwindcss` CLI.

- **Extensive Coverage:** We support all standard TailwindCSS class names,
Expand Down Expand Up @@ -231,5 +231,5 @@ to contribute to the code, please open an issue or pull request.

## License

`tw-macro` is licensed under the MIT license. See the `LICENSE` file for
`twust` is licensed under the MIT license. See the `LICENSE` file for
details.
2 changes: 1 addition & 1 deletion examples/leptos-demo/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ leptos_meta = { version = "0.5.0-beta2", features = ["csr", "nightly"] }
leptos_router = { version = "0.5.0-beta2", features = ["csr", "nightly"] }
log = "0.4"
gloo-net = { version = "0.2", features = ["http"] }
tw-macro = { git = "https://github.com/oyelowo/tailwind-rust", features = ["daisyui"]}
twust = { git = "https://github.com/oyelowo/twust", features = ["daisyui"] }


# dependecies for client (enable when csr or hydrate set)
Expand Down
2 changes: 1 addition & 1 deletion examples/leptos-demo/src/app.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use leptos::*;
use leptos_meta::*;
use leptos_router::*;
use tw_macro::tw;
use twust::tw;

#[component]
pub fn App() -> impl IntoView {
Expand Down
2 changes: 1 addition & 1 deletion tailwind/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ documentation.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
tw-macro = { workspace = true, features = ["daisyui"] }
twust = { workspace = true, features = ["daisyui"] }


serde = "1.0.188"
Expand Down
17 changes: 11 additions & 6 deletions tailwind/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* Copyright (c) 2023 Oyelowo Oyedayo
* Licensed under the MIT license
*/
use tw_macro::tw;
use twust::tw;

/// Invalid character in class name.
///
Expand Down Expand Up @@ -82,9 +82,9 @@ fn _unsupported_media_query() {}
///
/// ```compile_fail
/// use tw_macro::tw;
/// tw!("px-[45]");
/// tw!("px-45]");
/// ```
fn _missing_unit_after_arbitrary_value() {}
fn _malformed_arbitrary_value() {}

/// Invalid group usage.
///
Expand Down Expand Up @@ -166,10 +166,11 @@ fn _happy_paths() {
let _classnames = tw!("text-blue-600/[.07]");

// tw!("[something]");
let _classnames = tw!("px-[-45px]");
let _classnames = tw!("px-[45.43px]");
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]");
Expand All @@ -178,20 +179,24 @@ fn _happy_paths() {
let _classnames = tw!("px-[-45vmax]");
let _classnames = tw!("px-[-45mm]");
let _classnames = tw!("px-[-45pc]");
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!("-px-[45pc]");
let _classnames = tw!("hover:[mask-type:alpha]");
let _classnames = tw!(
Expand Down
118 changes: 117 additions & 1 deletion tailwind/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,126 @@
* Copyright (c) 2023 Oyelowo Oyedayo
* Licensed under the MIT license
*/
use tw_macro::tw;
use twust::tw;

fn main() {
let _ = tw!("btn btn");

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-[#fa5] text-[#bada55] hover:aria-checked:text-[22px]
text-[22.34e434cm]
before:content-['hello\_world']
grid grid-cols-[fit-content(theme(spacing.32))]
bg-[--my-color]
text-[var(--my-var)]
text-[length:var(--my-var)]
text-[color:var(--my-var)]
[--scroll-offset:56px] lg:[--scroll-offset:44px]
btn bg-[url('/img/down-arrow.svg')] ring-white/10 bg-black/25 bg-black/[80%] bg-black/[100] bg-black/[0.75] active:hover:collapse-arrow

[mask-image:linear-gradient(180deg,white,rgba(255,255,255,0))]

pt-8 text-base font-semibold leading-7

bg-[rgb(0,0,3)] absolute inset-0 bg-center

-mt-4

lg:[&:nth-child(3)]:hover:underline
group-[:nth-of-type(3)_&]:block
[&_p]:mt-4

flex [@supports(display:grid)]:grid
flex active:hover:[@supports(display:grid)]:grid

[@media(any-hover:hover){&:hover}]:opacity-100

hidden group-[.is-published]:block
group-[:nth-of-type(3)_&]:block
peer-[.is-dirty]:peer-required:block hidden
hidden peer-[:nth-of-type(3)_&]:block

group/edit invisible hover:bg-slate-200 group-hover/item:visible

peer-checked/published:text-sky-500

after:content-['*'] after:ml-0.5 after:text-red-500 block text-sm font-medium text-slate-700

before:content-[''] before:block
content-[>]
content-[<]

bg-black/75 supports-[backdrop-filter]:bg-black/25 supports-[backdrop-filter]:backdrop-blur

aria-[sort=ascending]:bg-[url('/img/down-arrow.svg')] aria-[sort=descending]:bg-[url('/img/up-arrow.svg')]


group-aria-[sort=ascending]:rotate-0 group-aria-[sort=descending]:rotate-180

data-[size=large]:p-8

open:bg-white dark:open:bg-slate-900 open:ring-1 open:ring-black/5 dark:open:ring-white/10 open:shadow-lg p-6 rounded-lg

lg:[&:nth-child(3)]:hover:underline

min-[320rem]:text-center max-[600px]:bg-sky-300

top-[117px] lg:top-[344px]

bg-[#bada55] text-[22px] before:content-['Festivus']


grid grid-cols-[fit-content(theme(spacing.32))]

bg-[--my-color]

[mask-type:luminance] hover:[mask-type:alpha]

[--scroll-offset:56px] lg:[--scroll-offset:44px]

lg:[&:nth-child(3)]:hover:underline
bg-[url('/what_a_rush.png')]
before:content-['hello\_world']
text-[22px]
text-[#bada55]
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


w-[calc(100%_-_theme("spacing[1.5]))"]
shadow-[inset_0_-3em_3em_rgba(0,_0,_0,_0.1),_0_0_0_2px_rgb(255,_255,_255),_0.3em_0.3em_1em_rgba(0,_0,_0,_0.3)]


"#
);
// 'content-[>]',
// // ^
// 'content-[<]',
// // ^
//
// // With functions and math expressions
// 'px-[calc(100%-1rem)]',
// 'px-[theme(spacing.1)]',
// 'px-[theme(spacing[1.5])]',
//
// // With spaces (replaced by `_`)
// 'bg-[rgb(255_0_0)]',
//
// // Examples with combinations
//

// let test =
// tw!("peer[.is-dirty]:peer-required:block hidden hidden peer-[:nth-of-type(3)_&]:block");
println!("TEXT - {}", test);
let _ = tw!("btn collapse-arrow");
tw!("bg-gray-600 bg-sky-700 bg-midnight underline");
tw!("bg-gray-600 aria-checked:bg-sky-700 aria-asc:bg-midnight data-checked:underline");
let _classnames = tw!("bg-taxvhiti bg-tahiti-500 bg-tahiti bg-midnight bg-red-50");
let _classnames = tw!("bg-taxvhiti bg-tahiti-500 bg-tahiti bg-midnight bg-purple bg-red-50 bg-tahiti-800 border-s-tahiti-800");
let _classnames = tw!("md:text-red-50 text-slate-50 text-purple text-tahiti-500");
Expand Down
3 changes: 2 additions & 1 deletion tw-macro/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "tw-macro"
name = "twust"
version = { workspace = true }
edition = { workspace = true }
authors = { workspace = true }
Expand All @@ -17,6 +17,7 @@ daisyui = []
syn = { workspace = true }
proc-macro2 = { workspace = true }
quote = { workspace = true }
nom = { workspace = true }
static_assertions = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
Expand Down
Loading