diff --git a/crates/biome_tailwind_parser/src/lexer/mod.rs b/crates/biome_tailwind_parser/src/lexer/mod.rs index f771c7921bc2..20546fea8935 100644 --- a/crates/biome_tailwind_parser/src/lexer/mod.rs +++ b/crates/biome_tailwind_parser/src/lexer/mod.rs @@ -6,6 +6,7 @@ use biome_parser::lexer::{Lexer, LexerCheckpoint, LexerWithCheckpoint, ReLexer, use biome_rowan::{SyntaxKind, TextLen}; use biome_tailwind_syntax::T; use biome_tailwind_syntax::TailwindSyntaxKind::*; +use biome_tailwind_syntax::metadata::BASENAMES_WITH_DASHES; use biome_tailwind_syntax::{TailwindSyntaxKind, TextSize}; pub(crate) struct TailwindLexer<'src> { @@ -104,6 +105,17 @@ impl<'src> TailwindLexer<'src> { fn consume_base(&mut self) -> TailwindSyntaxKind { self.assert_current_char_boundary(); + // Find the longest matching base name + let source_from_position = &self.source[self.position..]; + let base_name = BASENAMES_WITH_DASHES + .iter() + .rfind(|&name| source_from_position.starts_with(name)); + + if let Some(base_name) = base_name { + self.advance(base_name.len()); + return TW_BASE; + } + while let Some(byte) = self.current_byte() { let char = self.current_char_unchecked(); if char.is_whitespace() || byte == b'-' || byte == b'!' || byte == b':' { diff --git a/crates/biome_tailwind_parser/tests/tailwind_specs/ok/brackets/gradient.txt b/crates/biome_tailwind_parser/tests/tailwind_specs/ok/brackets/gradient.txt new file mode 100644 index 000000000000..7eec24e400f0 --- /dev/null +++ b/crates/biome_tailwind_parser/tests/tailwind_specs/ok/brackets/gradient.txt @@ -0,0 +1 @@ +bg-radial-[at_50%_75%] from-sky-200 via-blue-400 to-indigo-900 to-90% diff --git a/crates/biome_tailwind_parser/tests/tailwind_specs/ok/brackets/gradient.txt.snap b/crates/biome_tailwind_parser/tests/tailwind_specs/ok/brackets/gradient.txt.snap new file mode 100644 index 000000000000..de101155a6e4 --- /dev/null +++ b/crates/biome_tailwind_parser/tests/tailwind_specs/ok/brackets/gradient.txt.snap @@ -0,0 +1,149 @@ +--- +source: crates/biome_tailwind_parser/tests/spec_test.rs +expression: snapshot +--- +## Input + +```text +bg-radial-[at_50%_75%] from-sky-200 via-blue-400 to-indigo-900 to-90% + +``` + + +## AST + +``` +TwRoot { + bom_token: missing (optional), + candidates: TwCandidateList [ + TwFullCandidate { + variants: TwVariantList [], + candidate: TwFunctionalCandidate { + base_token: TW_BASE@0..9 "bg-radial" [] [], + minus_token: DASH@9..10 "-" [] [], + value: TwArbitraryValue { + l_brack_token: L_BRACKET@10..11 "[" [] [], + value_token: TW_VALUE@11..21 "at_50%_75%" [] [], + r_brack_token: R_BRACKET@21..22 "]" [] [], + }, + modifier: missing (optional), + }, + excl_token: missing (optional), + }, + WHITESPACE@22..23 " " [] [], + TwFullCandidate { + variants: TwVariantList [], + candidate: TwFunctionalCandidate { + base_token: TW_BASE@23..27 "from" [] [], + minus_token: DASH@27..28 "-" [] [], + value: TwNamedValue { + value_token: TW_VALUE@28..35 "sky-200" [] [], + }, + modifier: missing (optional), + }, + excl_token: missing (optional), + }, + WHITESPACE@35..36 " " [] [], + TwFullCandidate { + variants: TwVariantList [], + candidate: TwFunctionalCandidate { + base_token: TW_BASE@36..39 "via" [] [], + minus_token: DASH@39..40 "-" [] [], + value: TwNamedValue { + value_token: TW_VALUE@40..48 "blue-400" [] [], + }, + modifier: missing (optional), + }, + excl_token: missing (optional), + }, + WHITESPACE@48..49 " " [] [], + TwFullCandidate { + variants: TwVariantList [], + candidate: TwFunctionalCandidate { + base_token: TW_BASE@49..51 "to" [] [], + minus_token: DASH@51..52 "-" [] [], + value: TwNamedValue { + value_token: TW_VALUE@52..62 "indigo-900" [] [], + }, + modifier: missing (optional), + }, + excl_token: missing (optional), + }, + WHITESPACE@62..63 " " [] [], + TwFullCandidate { + variants: TwVariantList [], + candidate: TwFunctionalCandidate { + base_token: TW_BASE@63..65 "to" [] [], + minus_token: DASH@65..66 "-" [] [], + value: TwNamedValue { + value_token: TW_VALUE@66..70 "90%" [] [Newline("\n")], + }, + modifier: missing (optional), + }, + excl_token: missing (optional), + }, + ], + eof_token: EOF@70..70 "" [] [], +} +``` + +## CST + +``` +0: TW_ROOT@0..70 + 0: (empty) + 1: TW_CANDIDATE_LIST@0..70 + 0: TW_FULL_CANDIDATE@0..22 + 0: TW_VARIANT_LIST@0..0 + 1: TW_FUNCTIONAL_CANDIDATE@0..22 + 0: TW_BASE@0..9 "bg-radial" [] [] + 1: DASH@9..10 "-" [] [] + 2: TW_ARBITRARY_VALUE@10..22 + 0: L_BRACKET@10..11 "[" [] [] + 1: TW_VALUE@11..21 "at_50%_75%" [] [] + 2: R_BRACKET@21..22 "]" [] [] + 3: (empty) + 2: (empty) + 1: WHITESPACE@22..23 " " [] [] + 2: TW_FULL_CANDIDATE@23..35 + 0: TW_VARIANT_LIST@23..23 + 1: TW_FUNCTIONAL_CANDIDATE@23..35 + 0: TW_BASE@23..27 "from" [] [] + 1: DASH@27..28 "-" [] [] + 2: TW_NAMED_VALUE@28..35 + 0: TW_VALUE@28..35 "sky-200" [] [] + 3: (empty) + 2: (empty) + 3: WHITESPACE@35..36 " " [] [] + 4: TW_FULL_CANDIDATE@36..48 + 0: TW_VARIANT_LIST@36..36 + 1: TW_FUNCTIONAL_CANDIDATE@36..48 + 0: TW_BASE@36..39 "via" [] [] + 1: DASH@39..40 "-" [] [] + 2: TW_NAMED_VALUE@40..48 + 0: TW_VALUE@40..48 "blue-400" [] [] + 3: (empty) + 2: (empty) + 5: WHITESPACE@48..49 " " [] [] + 6: TW_FULL_CANDIDATE@49..62 + 0: TW_VARIANT_LIST@49..49 + 1: TW_FUNCTIONAL_CANDIDATE@49..62 + 0: TW_BASE@49..51 "to" [] [] + 1: DASH@51..52 "-" [] [] + 2: TW_NAMED_VALUE@52..62 + 0: TW_VALUE@52..62 "indigo-900" [] [] + 3: (empty) + 2: (empty) + 7: WHITESPACE@62..63 " " [] [] + 8: TW_FULL_CANDIDATE@63..70 + 0: TW_VARIANT_LIST@63..63 + 1: TW_FUNCTIONAL_CANDIDATE@63..70 + 0: TW_BASE@63..65 "to" [] [] + 1: DASH@65..66 "-" [] [] + 2: TW_NAMED_VALUE@66..70 + 0: TW_VALUE@66..70 "90%" [] [Newline("\n")] + 3: (empty) + 2: (empty) + 2: EOF@70..70 "" [] [] + +``` diff --git a/crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/base-has-dash-0.txt b/crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/base-has-dash-0.txt new file mode 100644 index 000000000000..5db3ac391f28 --- /dev/null +++ b/crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/base-has-dash-0.txt @@ -0,0 +1 @@ +drop-shadow-red-500 diff --git a/crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/base-has-dash-0.txt.snap b/crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/base-has-dash-0.txt.snap new file mode 100644 index 000000000000..3a803c97f65e --- /dev/null +++ b/crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/base-has-dash-0.txt.snap @@ -0,0 +1,53 @@ +--- +source: crates/biome_tailwind_parser/tests/spec_test.rs +expression: snapshot +--- +## Input + +```text +drop-shadow-red-500 + +``` + + +## AST + +``` +TwRoot { + bom_token: missing (optional), + candidates: TwCandidateList [ + TwFullCandidate { + variants: TwVariantList [], + candidate: TwFunctionalCandidate { + base_token: TW_BASE@0..11 "drop-shadow" [] [], + minus_token: DASH@11..12 "-" [] [], + value: TwNamedValue { + value_token: TW_VALUE@12..20 "red-500" [] [Newline("\n")], + }, + modifier: missing (optional), + }, + excl_token: missing (optional), + }, + ], + eof_token: EOF@20..20 "" [] [], +} +``` + +## CST + +``` +0: TW_ROOT@0..20 + 0: (empty) + 1: TW_CANDIDATE_LIST@0..20 + 0: TW_FULL_CANDIDATE@0..20 + 0: TW_VARIANT_LIST@0..0 + 1: TW_FUNCTIONAL_CANDIDATE@0..20 + 0: TW_BASE@0..11 "drop-shadow" [] [] + 1: DASH@11..12 "-" [] [] + 2: TW_NAMED_VALUE@12..20 + 0: TW_VALUE@12..20 "red-500" [] [Newline("\n")] + 3: (empty) + 2: (empty) + 2: EOF@20..20 "" [] [] + +``` diff --git a/crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/base-has-dash-1.txt b/crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/base-has-dash-1.txt new file mode 100644 index 000000000000..d66d2ff841d2 --- /dev/null +++ b/crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/base-has-dash-1.txt @@ -0,0 +1 @@ +drop-shadow-[#000000] diff --git a/crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/base-has-dash-1.txt.snap b/crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/base-has-dash-1.txt.snap new file mode 100644 index 000000000000..d1d95962acc6 --- /dev/null +++ b/crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/base-has-dash-1.txt.snap @@ -0,0 +1,57 @@ +--- +source: crates/biome_tailwind_parser/tests/spec_test.rs +expression: snapshot +--- +## Input + +```text +drop-shadow-[#000000] + +``` + + +## AST + +``` +TwRoot { + bom_token: missing (optional), + candidates: TwCandidateList [ + TwFullCandidate { + variants: TwVariantList [], + candidate: TwFunctionalCandidate { + base_token: TW_BASE@0..11 "drop-shadow" [] [], + minus_token: DASH@11..12 "-" [] [], + value: TwArbitraryValue { + l_brack_token: L_BRACKET@12..13 "[" [] [], + value_token: TW_VALUE@13..20 "#000000" [] [], + r_brack_token: R_BRACKET@20..22 "]" [] [Newline("\n")], + }, + modifier: missing (optional), + }, + excl_token: missing (optional), + }, + ], + eof_token: EOF@22..22 "" [] [], +} +``` + +## CST + +``` +0: TW_ROOT@0..22 + 0: (empty) + 1: TW_CANDIDATE_LIST@0..22 + 0: TW_FULL_CANDIDATE@0..22 + 0: TW_VARIANT_LIST@0..0 + 1: TW_FUNCTIONAL_CANDIDATE@0..22 + 0: TW_BASE@0..11 "drop-shadow" [] [] + 1: DASH@11..12 "-" [] [] + 2: TW_ARBITRARY_VALUE@12..22 + 0: L_BRACKET@12..13 "[" [] [] + 1: TW_VALUE@13..20 "#000000" [] [] + 2: R_BRACKET@20..22 "]" [] [Newline("\n")] + 3: (empty) + 2: (empty) + 2: EOF@22..22 "" [] [] + +``` diff --git a/crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/base-has-dash-2.txt b/crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/base-has-dash-2.txt new file mode 100644 index 000000000000..f8c7f5b8a08d --- /dev/null +++ b/crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/base-has-dash-2.txt @@ -0,0 +1 @@ +border-spacing-y-[2px] diff --git a/crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/base-has-dash-2.txt.snap b/crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/base-has-dash-2.txt.snap new file mode 100644 index 000000000000..1a395d2644b7 --- /dev/null +++ b/crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/base-has-dash-2.txt.snap @@ -0,0 +1,57 @@ +--- +source: crates/biome_tailwind_parser/tests/spec_test.rs +expression: snapshot +--- +## Input + +```text +border-spacing-y-[2px] + +``` + + +## AST + +``` +TwRoot { + bom_token: missing (optional), + candidates: TwCandidateList [ + TwFullCandidate { + variants: TwVariantList [], + candidate: TwFunctionalCandidate { + base_token: TW_BASE@0..16 "border-spacing-y" [] [], + minus_token: DASH@16..17 "-" [] [], + value: TwArbitraryValue { + l_brack_token: L_BRACKET@17..18 "[" [] [], + value_token: TW_VALUE@18..21 "2px" [] [], + r_brack_token: R_BRACKET@21..23 "]" [] [Newline("\n")], + }, + modifier: missing (optional), + }, + excl_token: missing (optional), + }, + ], + eof_token: EOF@23..23 "" [] [], +} +``` + +## CST + +``` +0: TW_ROOT@0..23 + 0: (empty) + 1: TW_CANDIDATE_LIST@0..23 + 0: TW_FULL_CANDIDATE@0..23 + 0: TW_VARIANT_LIST@0..0 + 1: TW_FUNCTIONAL_CANDIDATE@0..23 + 0: TW_BASE@0..16 "border-spacing-y" [] [] + 1: DASH@16..17 "-" [] [] + 2: TW_ARBITRARY_VALUE@17..23 + 0: L_BRACKET@17..18 "[" [] [] + 1: TW_VALUE@18..21 "2px" [] [] + 2: R_BRACKET@21..23 "]" [] [Newline("\n")] + 3: (empty) + 2: (empty) + 2: EOF@23..23 "" [] [] + +``` diff --git a/crates/biome_tailwind_parser/tests/tailwind_specs/ok/stress/stress-2.txt.snap b/crates/biome_tailwind_parser/tests/tailwind_specs/ok/stress/stress-2.txt.snap index a06e2a6330fe..db2a94817939 100644 --- a/crates/biome_tailwind_parser/tests/tailwind_specs/ok/stress/stress-2.txt.snap +++ b/crates/biome_tailwind_parser/tests/tailwind_specs/ok/stress/stress-2.txt.snap @@ -45,10 +45,10 @@ TwRoot { TwFullCandidate { variants: TwVariantList [], candidate: TwFunctionalCandidate { - base_token: TW_BASE@34..38 "ring" [] [], - minus_token: DASH@38..39 "-" [] [], + base_token: TW_BASE@34..45 "ring-offset" [] [], + minus_token: DASH@45..46 "-" [] [], value: TwNamedValue { - value_token: TW_VALUE@39..56 "offset-background" [] [], + value_token: TW_VALUE@46..56 "background" [] [], }, modifier: missing (optional), }, @@ -176,10 +176,10 @@ TwRoot { COLON@175..176 ":" [] [], ], candidate: TwFunctionalCandidate { - base_token: TW_BASE@176..180 "ring" [] [], - minus_token: DASH@180..181 "-" [] [], + base_token: TW_BASE@176..187 "ring-offset" [] [], + minus_token: DASH@187..188 "-" [] [], value: TwNamedValue { - value_token: TW_VALUE@181..189 "offset-2" [] [], + value_token: TW_VALUE@188..189 "2" [] [], }, modifier: missing (optional), }, @@ -255,10 +255,10 @@ TwRoot { 4: TW_FULL_CANDIDATE@34..56 0: TW_VARIANT_LIST@34..34 1: TW_FUNCTIONAL_CANDIDATE@34..56 - 0: TW_BASE@34..38 "ring" [] [] - 1: DASH@38..39 "-" [] [] - 2: TW_NAMED_VALUE@39..56 - 0: TW_VALUE@39..56 "offset-background" [] [] + 0: TW_BASE@34..45 "ring-offset" [] [] + 1: DASH@45..46 "-" [] [] + 2: TW_NAMED_VALUE@46..56 + 0: TW_VALUE@46..56 "background" [] [] 3: (empty) 2: (empty) 5: WHITESPACE@56..57 " " [] [] @@ -352,10 +352,10 @@ TwRoot { 0: TW_VALUE@168..175 "visible" [] [] 1: COLON@175..176 ":" [] [] 1: TW_FUNCTIONAL_CANDIDATE@176..189 - 0: TW_BASE@176..180 "ring" [] [] - 1: DASH@180..181 "-" [] [] - 2: TW_NAMED_VALUE@181..189 - 0: TW_VALUE@181..189 "offset-2" [] [] + 0: TW_BASE@176..187 "ring-offset" [] [] + 1: DASH@187..188 "-" [] [] + 2: TW_NAMED_VALUE@188..189 + 0: TW_VALUE@188..189 "2" [] [] 3: (empty) 2: (empty) 21: WHITESPACE@189..190 " " [] [] diff --git a/crates/biome_tailwind_syntax/src/lib.rs b/crates/biome_tailwind_syntax/src/lib.rs index 20476810ffb7..56d2e7aaaae7 100644 --- a/crates/biome_tailwind_syntax/src/lib.rs +++ b/crates/biome_tailwind_syntax/src/lib.rs @@ -2,6 +2,7 @@ #[macro_use] mod generated; +pub mod metadata; mod syntax_node; pub use self::generated::*; diff --git a/crates/biome_tailwind_syntax/src/metadata.rs b/crates/biome_tailwind_syntax/src/metadata.rs new file mode 100644 index 000000000000..e0f9e930271b --- /dev/null +++ b/crates/biome_tailwind_syntax/src/metadata.rs @@ -0,0 +1,1337 @@ +/// Base names that will parse wrong because they contain dashes. +pub static BASENAMES_WITH_DASHES: &[&str] = &[ + "any-pointer", + "auto-cols", + "auto-rows", + "backdrop-blur", + "backdrop-brightness", + "backdrop-contrast", + "backdrop-filter", + "backdrop-grayscale", + "backdrop-hue-rotate", + "backdrop-invert", + "backdrop-opacity", + "backdrop-saturate", + "backdrop-sepia", + "bg-conic", + "bg-linear", + "bg-position", + "bg-radial", + "bg-size", + "border-b", + "border-e", + "border-l", + "border-r", + "border-s", + "border-spacing", + "border-spacing-x", + "border-spacing-y", + "border-t", + "border-x", + "border-y", + "col-end", + "col-span", + "col-start", + "divide-x", + "divide-y", + "drop-shadow", + "font-stretch", + "gap-x", + "gap-y", + "grid-cols", + "grid-rows", + "hue-rotate", + "inset-ring", + "inset-shadow", + "line-clamp", + "list-image", + "mask-b-from", + "mask-b-to", + "mask-conic", + "mask-conic-from", + "mask-conic-to", + "mask-l-from", + "mask-l-to", + "mask-linear", + "mask-linear-from", + "mask-linear-to", + "mask-position", + "mask-r-from", + "mask-r-to", + "mask-radial", + "mask-radial-at", + "mask-radial-from", + "mask-radial-to", + "mask-size", + "mask-t-from", + "mask-t-to", + "mask-x-from", + "mask-x-to", + "mask-y-from", + "mask-y-to", + "max-h", + "max-w", + "min-h", + "min-w", + "not-sr-only", + "not-supports", + "nth-last", + "nth-last-of-type", + "nth-of-type", + "outline-offset", + "perspective-origin", + "pointer-events", + "ring-offset", + "rotate-x", + "rotate-y", + "rotate-z", + "rounded-b", + "rounded-bl", + "rounded-br", + "rounded-e", + "rounded-ee", + "rounded-es", + "rounded-l", + "rounded-r", + "rounded-s", + "rounded-se", + "rounded-ss", + "rounded-t", + "rounded-tl", + "rounded-tr", + "row-end", + "row-span", + "row-start", + "scale-x", + "scale-y", + "scale-z", + "scroll-m", + "scroll-mb", + "scroll-me", + "scroll-ml", + "scroll-mr", + "scroll-ms", + "scroll-mt", + "scroll-mx", + "scroll-my", + "scroll-p", + "scroll-pb", + "scroll-pe", + "scroll-pl", + "scroll-pr", + "scroll-ps", + "scroll-pt", + "scroll-px", + "scroll-py", + "skew-x", + "skew-y", + "space-x", + "space-y", + "sr-only", + "text-shadow", + "translate-x", + "translate-y", + "translate-z", + "underline-offset", + "will-change", +]; + +// Static utilities that don't take parameters +pub static KNOWN_STATIC_UTILITIES: &[&str] = &[ + // Screen reader utilities + "sr-only", + "not-sr-only", + // Pointer events + "pointer-events-none", + "pointer-events-auto", + // Visibility + "visible", + "invisible", + "collapse", + // Position + "static", + "fixed", + "absolute", + "relative", + "sticky", + // Isolation + "isolate", + "isolation-auto", + // Z-index + "z-auto", + // Order + "order-first", + "order-last", + // Grid column + "col-auto", + "col-span-full", + "col-start-auto", + "col-end-auto", + // Grid row + "row-auto", + "row-span-full", + "row-start-auto", + "row-end-auto", + // Float + "float-start", + "float-end", + "float-right", + "float-left", + "float-none", + // Clear + "clear-start", + "clear-end", + "clear-right", + "clear-left", + "clear-both", + "clear-none", + // Box sizing + "box-border", + "box-content", + // Line clamp + "line-clamp-none", + // Display + "block", + "inline-block", + "inline", + "hidden", + "inline-flex", + "table", + "inline-table", + "table-caption", + "table-cell", + "table-column", + "table-column-group", + "table-footer-group", + "table-header-group", + "table-row-group", + "table-row", + "flow-root", + "flex", + "grid", + "inline-grid", + "contents", + "list-item", + // Field sizing + "field-sizing-content", + "field-sizing-fixed", + // Aspect ratio + "aspect-auto", + "aspect-square", + // Size, width, height utilities with static values + "size-full", + "size-svw", + "size-lvw", + "size-dvw", + "size-svh", + "size-lvh", + "size-dvh", + "size-min", + "size-max", + "size-fit", + "size-auto", + "w-full", + "w-svw", + "w-lvw", + "w-dvw", + "w-svh", + "w-lvh", + "w-dvh", + "w-min", + "w-max", + "w-fit", + "w-auto", + "w-screen", + "h-full", + "h-svw", + "h-lvw", + "h-dvw", + "h-svh", + "h-lvh", + "h-dvh", + "h-min", + "h-max", + "h-fit", + "h-auto", + "h-lh", + "h-screen", + "min-w-full", + "min-w-svw", + "min-w-lvw", + "min-w-dvw", + "min-w-svh", + "min-w-lvh", + "min-w-dvh", + "min-w-min", + "min-w-max", + "min-w-fit", + "min-w-auto", + "min-w-screen", + "min-h-full", + "min-h-svw", + "min-h-lvw", + "min-h-dvw", + "min-h-svh", + "min-h-lvh", + "min-h-dvh", + "min-h-min", + "min-h-max", + "min-h-fit", + "min-h-auto", + "min-h-lh", + "min-h-screen", + "max-w-full", + "max-w-svw", + "max-w-lvw", + "max-w-dvw", + "max-w-svh", + "max-w-lvh", + "max-w-dvh", + "max-w-min", + "max-w-max", + "max-w-fit", + "max-w-none", + "max-w-screen", + "max-h-full", + "max-h-svw", + "max-h-lvw", + "max-h-dvw", + "max-h-svh", + "max-h-lvh", + "max-h-dvh", + "max-h-min", + "max-h-max", + "max-h-fit", + "max-h-none", + "max-h-lh", + "max-h-screen", + // Container + "container", + // Flex + "flex-auto", + "flex-initial", + "flex-none", + // Table layout + "table-auto", + "table-fixed", + // Caption side + "caption-top", + "caption-bottom", + // Border collapse + "border-collapse", + "border-separate", + // Transform origin + "origin-center", + "origin-top", + "origin-top-right", + "origin-right", + "origin-bottom-right", + "origin-bottom", + "origin-bottom-left", + "origin-left", + "origin-top-left", + // Perspective origin + "perspective-origin-center", + "perspective-origin-top", + "perspective-origin-top-right", + "perspective-origin-right", + "perspective-origin-bottom-right", + "perspective-origin-bottom", + "perspective-origin-bottom-left", + "perspective-origin-left", + "perspective-origin-top-left", + // Perspective + "perspective-none", + // Translate + "translate-none", + "-translate-full", + "translate-full", + "-translate-x-full", + "translate-x-full", + "-translate-y-full", + "translate-y-full", + "translate-3d", + // Scale + "scale-none", + "scale-3d", + // Rotate + "rotate-none", + // Transform + "transform-cpu", + "transform-gpu", + "transform-none", + // Transform style + "transform-flat", + "transform-3d", + // Transform box + "transform-content", + "transform-border", + "transform-fill", + "transform-stroke", + "transform-view", + // Backface visibility + "backface-visible", + "backface-hidden", + // Cursor values + "cursor-auto", + "cursor-default", + "cursor-pointer", + "cursor-wait", + "cursor-text", + "cursor-move", + "cursor-help", + "cursor-not-allowed", + "cursor-none", + "cursor-context-menu", + "cursor-progress", + "cursor-cell", + "cursor-crosshair", + "cursor-vertical-text", + "cursor-alias", + "cursor-copy", + "cursor-no-drop", + "cursor-grab", + "cursor-grabbing", + "cursor-all-scroll", + "cursor-col-resize", + "cursor-row-resize", + "cursor-n-resize", + "cursor-e-resize", + "cursor-s-resize", + "cursor-w-resize", + "cursor-ne-resize", + "cursor-nw-resize", + "cursor-se-resize", + "cursor-sw-resize", + "cursor-ew-resize", + "cursor-ns-resize", + "cursor-nesw-resize", + "cursor-nwse-resize", + "cursor-zoom-in", + "cursor-zoom-out", + // Touch action + "touch-auto", + "touch-none", + "touch-manipulation", + "touch-pan-x", + "touch-pan-left", + "touch-pan-right", + "touch-pan-y", + "touch-pan-up", + "touch-pan-down", + "touch-pinch-zoom", + // User select + "select-none", + "select-text", + "select-all", + "select-auto", + // Resize + "resize-none", + "resize-x", + "resize-y", + "resize", + // Scroll snap + "snap-none", + "snap-x", + "snap-y", + "snap-both", + "snap-mandatory", + "snap-proximity", + "snap-align-none", + "snap-start", + "snap-end", + "snap-center", + "snap-normal", + "snap-always", + // List style + "list-inside", + "list-outside", + "list-none", + "list-disc", + "list-decimal", + "list-image-none", + // Appearance + "appearance-none", + "appearance-auto", + // Color scheme + "scheme-normal", + "scheme-dark", + "scheme-light", + "scheme-light-dark", + "scheme-only-dark", + "scheme-only-light", + // Columns + "columns-auto", + // Break + "break-before-auto", + "break-before-avoid", + "break-before-all", + "break-before-avoid-page", + "break-before-page", + "break-before-left", + "break-before-right", + "break-before-column", + "break-inside-auto", + "break-inside-avoid", + "break-inside-avoid-page", + "break-inside-avoid-column", + "break-after-auto", + "break-after-avoid", + "break-after-all", + "break-after-avoid-page", + "break-after-page", + "break-after-left", + "break-after-right", + "break-after-column", + // Grid flow + "grid-flow-row", + "grid-flow-col", + "grid-flow-dense", + "grid-flow-row-dense", + "grid-flow-col-dense", + // Auto columns + "auto-cols-auto", + "auto-cols-min", + "auto-cols-max", + "auto-cols-fr", + // Auto rows + "auto-rows-auto", + "auto-rows-min", + "auto-rows-max", + "auto-rows-fr", + // Grid template + "grid-cols-none", + "grid-cols-subgrid", + "grid-rows-none", + "grid-rows-subgrid", + // Flex direction + "flex-row", + "flex-row-reverse", + "flex-col", + "flex-col-reverse", + // Flex wrap + "flex-wrap", + "flex-nowrap", + "flex-wrap-reverse", + // Place content + "place-content-center", + "place-content-start", + "place-content-end", + "place-content-center-safe", + "place-content-end-safe", + "place-content-between", + "place-content-around", + "place-content-evenly", + "place-content-baseline", + "place-content-stretch", + // Place items + "place-items-center", + "place-items-start", + "place-items-end", + "place-items-center-safe", + "place-items-end-safe", + "place-items-baseline", + "place-items-stretch", + // Align content + "content-normal", + "content-center", + "content-start", + "content-end", + "content-center-safe", + "content-end-safe", + "content-between", + "content-around", + "content-evenly", + "content-baseline", + "content-stretch", + // Align items + "items-center", + "items-start", + "items-end", + "items-center-safe", + "items-end-safe", + "items-baseline", + "items-baseline-last", + "items-stretch", + // Justify content + "justify-normal", + "justify-center", + "justify-start", + "justify-end", + "justify-center-safe", + "justify-end-safe", + "justify-between", + "justify-around", + "justify-evenly", + "justify-baseline", + "justify-stretch", + // Justify items + "justify-items-normal", + "justify-items-center", + "justify-items-start", + "justify-items-end", + "justify-items-center-safe", + "justify-items-end-safe", + "justify-items-stretch", + // Place self + "place-self-auto", + "place-self-start", + "place-self-end", + "place-self-center", + "place-self-end-safe", + "place-self-center-safe", + "place-self-stretch", + // Align self + "self-auto", + "self-start", + "self-end", + "self-center", + "self-end-safe", + "self-center-safe", + "self-stretch", + "self-baseline", + "self-baseline-last", + // Justify self + "justify-self-auto", + "justify-self-start", + "justify-self-end", + "justify-self-center", + "justify-self-end-safe", + "justify-self-center-safe", + "justify-self-stretch", + // Overflow + "overflow-auto", + "overflow-hidden", + "overflow-clip", + "overflow-visible", + "overflow-scroll", + "overflow-x-auto", + "overflow-x-hidden", + "overflow-x-clip", + "overflow-x-visible", + "overflow-x-scroll", + "overflow-y-auto", + "overflow-y-hidden", + "overflow-y-clip", + "overflow-y-visible", + "overflow-y-scroll", + // Overscroll + "overscroll-auto", + "overscroll-contain", + "overscroll-none", + "overscroll-x-auto", + "overscroll-x-contain", + "overscroll-x-none", + "overscroll-y-auto", + "overscroll-y-contain", + "overscroll-y-none", + // Scroll behavior + "scroll-auto", + "scroll-smooth", + // Text overflow + "truncate", + "text-ellipsis", + "text-clip", + // Hyphens + "hyphens-none", + "hyphens-manual", + "hyphens-auto", + // Whitespace + "whitespace-normal", + "whitespace-nowrap", + "whitespace-pre", + "whitespace-pre-line", + "whitespace-pre-wrap", + "whitespace-break-spaces", + // Text wrap + "text-wrap", + "text-nowrap", + "text-balance", + "text-pretty", + // Word break + "break-normal", + "break-words", + "break-all", + "break-keep", + // Overflow wrap + "wrap-anywhere", + "wrap-break-word", + "wrap-normal", + // Border radius + "rounded-none", + "rounded-full", + "rounded-s-none", + "rounded-s-full", + "rounded-e-none", + "rounded-e-full", + "rounded-t-none", + "rounded-t-full", + "rounded-r-none", + "rounded-r-full", + "rounded-b-none", + "rounded-b-full", + "rounded-l-none", + "rounded-l-full", + "rounded-ss-none", + "rounded-ss-full", + "rounded-se-none", + "rounded-se-full", + "rounded-ee-none", + "rounded-ee-full", + "rounded-es-none", + "rounded-es-full", + "rounded-tl-none", + "rounded-tl-full", + "rounded-tr-none", + "rounded-tr-full", + "rounded-br-none", + "rounded-br-full", + "rounded-bl-none", + "rounded-bl-full", + // Border style + "border-solid", + "border-dashed", + "border-dotted", + "border-double", + "border-hidden", + "border-none", + // Divide style + "divide-solid", + "divide-dashed", + "divide-dotted", + "divide-double", + "divide-none", + "divide-x-reverse", + "divide-y-reverse", + // Background size + "bg-auto", + "bg-cover", + "bg-contain", + // Background attachment + "bg-fixed", + "bg-local", + "bg-scroll", + // Background position + "bg-top", + "bg-top-left", + "bg-top-right", + "bg-bottom", + "bg-bottom-left", + "bg-bottom-right", + "bg-left", + "bg-right", + "bg-center", + // Background repeat + "bg-repeat", + "bg-no-repeat", + "bg-repeat-x", + "bg-repeat-y", + "bg-repeat-round", + "bg-repeat-space", + // Background image + "bg-none", + // Background blend mode + "bg-blend-normal", + "bg-blend-multiply", + "bg-blend-screen", + "bg-blend-overlay", + "bg-blend-darken", + "bg-blend-lighten", + "bg-blend-color-dodge", + "bg-blend-color-burn", + "bg-blend-hard-light", + "bg-blend-soft-light", + "bg-blend-difference", + "bg-blend-exclusion", + "bg-blend-hue", + "bg-blend-saturation", + "bg-blend-color", + "bg-blend-luminosity", + // Mix blend mode + "mix-blend-normal", + "mix-blend-multiply", + "mix-blend-screen", + "mix-blend-overlay", + "mix-blend-darken", + "mix-blend-lighten", + "mix-blend-color-dodge", + "mix-blend-color-burn", + "mix-blend-hard-light", + "mix-blend-soft-light", + "mix-blend-difference", + "mix-blend-exclusion", + "mix-blend-hue", + "mix-blend-saturation", + "mix-blend-color", + "mix-blend-luminosity", + "mix-blend-plus-darker", + "mix-blend-plus-lighter", + // Via gradient + "via-none", + // Fill + "fill-none", + // Stroke + "stroke-none", + // Object fit + "object-contain", + "object-cover", + "object-fill", + "object-none", + "object-scale-down", + // Object position + "object-top", + "object-top-left", + "object-top-right", + "object-bottom", + "object-bottom-left", + "object-bottom-right", + "object-left", + "object-right", + "object-center", + // Text align + "text-left", + "text-center", + "text-right", + "text-justify", + "text-start", + "text-end", + // Vertical align + "align-baseline", + "align-top", + "align-middle", + "align-bottom", + "align-text-top", + "align-text-bottom", + "align-sub", + "align-super", + // Text transform + "uppercase", + "lowercase", + "capitalize", + "normal-case", + // Font style + "italic", + "not-italic", + // Text decoration + "underline", + "overline", + "line-through", + "no-underline", + // Font stretch + "font-stretch-normal", + "font-stretch-ultra-condensed", + "font-stretch-extra-condensed", + "font-stretch-condensed", + "font-stretch-semi-condensed", + "font-stretch-semi-expanded", + "font-stretch-expanded", + "font-stretch-extra-expanded", + "font-stretch-ultra-expanded", + // Text decoration style + "decoration-solid", + "decoration-double", + "decoration-dotted", + "decoration-dashed", + "decoration-wavy", + // Text decoration thickness + "decoration-auto", + "decoration-from-font", + // Animation + "animate-none", + // Filter + "blur-none", + "backdrop-blur-none", + // Box decoration break + "box-decoration-slice", + "box-decoration-clone", + // Background clip + "bg-clip-text", + "bg-clip-border", + "bg-clip-padding", + "bg-clip-content", + // Background origin + "bg-origin-border", + "bg-origin-padding", + "bg-origin-content", + // Mask + "mask-none", + "mask-add", + "mask-subtract", + "mask-intersect", + "mask-exclude", + "mask-alpha", + "mask-luminance", + "mask-match", + "mask-type-alpha", + "mask-type-luminance", + "mask-auto", + "mask-cover", + "mask-contain", + "mask-top", + "mask-top-left", + "mask-top-right", + "mask-bottom", + "mask-bottom-left", + "mask-bottom-right", + "mask-left", + "mask-right", + "mask-center", + "mask-repeat", + "mask-no-repeat", + "mask-repeat-x", + "mask-repeat-y", + "mask-repeat-round", + "mask-repeat-space", + "mask-clip-border", + "mask-clip-padding", + "mask-clip-content", + "mask-clip-fill", + "mask-clip-stroke", + "mask-clip-view", + "mask-no-clip", + "mask-origin-border", + "mask-origin-padding", + "mask-origin-content", + "mask-origin-fill", + "mask-origin-stroke", + "mask-origin-view", + "mask-circle", + "mask-ellipse", + "mask-radial-closest-side", + "mask-radial-farthest-side", + "mask-radial-closest-corner", + "mask-radial-farthest-corner", + "mask-radial-at-top", + "mask-radial-at-top-left", + "mask-radial-at-top-right", + "mask-radial-at-bottom", + "mask-radial-at-bottom-left", + "mask-radial-at-bottom-right", + "mask-radial-at-left", + "mask-radial-at-right", + "mask-radial-at-center", + // Transition + "transition-none", + "transition-all", + "transition-colors", + "transition-opacity", + "transition-shadow", + "transition-transform", + "transition-discrete", + "transition-normal", + // Timing functions + "ease-initial", + "ease-linear", + // Duration + "duration-initial", + // Will change + "will-change-auto", + "will-change-scroll", + "will-change-contents", + "will-change-transform", + // Content + "content-none", + // Contain + "contain-none", + "contain-content", + "contain-strict", + "contain-size", + "contain-inline-size", + "contain-layout", + "contain-paint", + "contain-style", + // Forced color adjust + "forced-color-adjust-none", + "forced-color-adjust-auto", + // Line height + "leading-none", + // Font smoothing + "antialiased", + "subpixel-antialiased", + // Font variant numeric + "normal-nums", + "ordinal", + "slashed-zero", + "lining-nums", + "oldstyle-nums", + "proportional-nums", + "tabular-nums", + "diagonal-fractions", + "stacked-fractions", + // Outline + "outline-hidden", + "outline-none", + "outline-solid", + "outline-dashed", + "outline-dotted", + "outline-double", + // Text underline offset + "underline-offset-auto", + // Text shadow + "text-shadow-initial", + // Box shadow + "shadow-initial", + "inset-shadow-initial", + // Ring + "ring-inset", + // Accent color + "accent-auto", + // Space + "space-x-reverse", + "space-y-reverse", + // Drop shadow + "drop-shadow-none", +]; + +// Functional utilities that take parameters (like w-*, bg-*, etc.) +pub static KNOWN_FUNCTIONAL_UTILITIES: &[&str] = &[ + // Inset utilities + "inset", + "inset-x", + "inset-y", + "start", + "end", + "top", + "right", + "bottom", + "left", + // Z-index + "z", + // Order + "order", + // Grid column + "col", + "col-span", + "col-start", + "col-end", + // Grid row + "row", + "row-span", + "row-start", + "row-end", + // Margin utilities + "m", + "mx", + "my", + "ms", + "me", + "mt", + "mr", + "mb", + "ml", + // Line clamp + "line-clamp", + // Aspect ratio + "aspect", + // Size utilities + "size", + "w", + "min-w", + "max-w", + "h", + "min-h", + "max-h", + // Flex utilities + "flex", + "shrink", + "grow", + "basis", + // Border spacing + "border-spacing", + "border-spacing-x", + "border-spacing-y", + // Transform origin + "origin", + "perspective-origin", + // Perspective + "perspective", + // Translate + "translate", + "translate-x", + "translate-y", + "translate-z", + // Scale + "scale", + "scale-x", + "scale-y", + "scale-z", + // Rotate + "rotate", + "rotate-x", + "rotate-y", + "rotate-z", + // Skew + "skew", + "skew-x", + "skew-y", + // Transform + "transform", + // Cursor + "cursor", + // Columns + "columns", + // Auto columns + "auto-cols", + // Auto rows + "auto-rows", + // Grid template + "grid-cols", + "grid-rows", + // Gap + "gap", + "gap-x", + "gap-y", + // Space + "space-x", + "space-y", + // Scroll margin + "scroll-m", + "scroll-mx", + "scroll-my", + "scroll-ms", + "scroll-me", + "scroll-mt", + "scroll-mr", + "scroll-mb", + "scroll-ml", + // Scroll padding + "scroll-p", + "scroll-px", + "scroll-py", + "scroll-ps", + "scroll-pe", + "scroll-pt", + "scroll-pr", + "scroll-pb", + "scroll-pl", + // List style + "list", + "list-image", + // Border radius + "rounded", + "rounded-s", + "rounded-e", + "rounded-t", + "rounded-r", + "rounded-b", + "rounded-l", + "rounded-ss", + "rounded-se", + "rounded-ee", + "rounded-es", + "rounded-tl", + "rounded-tr", + "rounded-br", + "rounded-bl", + // Border utilities + "border", + "border-x", + "border-y", + "border-s", + "border-e", + "border-t", + "border-r", + "border-b", + "border-l", + // Divide utilities + "divide-x", + "divide-y", + // Background utilities + "bg-size", + "bg-position", + "bg-linear", + "bg-conic", + "bg-radial", + "bg", + // Gradient stops + "from", + "via", + "to", + // Mask utilities + "mask", + "mask-size", + "mask-position", + "mask-linear", + "mask-linear-from", + "mask-linear-to", + "mask-radial-at", + "mask-radial", + "mask-radial-from", + "mask-radial-to", + "mask-conic", + "mask-conic-from", + "mask-conic-to", + "mask-x-from", + "mask-x-to", + "mask-y-from", + "mask-y-to", + "mask-t-from", + "mask-t-to", + "mask-r-from", + "mask-r-to", + "mask-b-from", + "mask-b-to", + "mask-l-from", + "mask-l-to", + // Fill and stroke + "fill", + "stroke", + // Object position + "object", + // Padding utilities + "p", + "px", + "py", + "ps", + "pe", + "pt", + "pr", + "pb", + "pl", + // Text indent + "indent", + // Vertical align + "align", + // Font utilities + "font", + "font-stretch", + // Text decoration + "decoration", + // Animation + "animate", + // Filter utilities + "filter", + "backdrop-filter", + "blur", + "backdrop-blur", + "brightness", + "backdrop-brightness", + "contrast", + "backdrop-contrast", + "grayscale", + "backdrop-grayscale", + "hue-rotate", + "backdrop-hue-rotate", + "invert", + "backdrop-invert", + "saturate", + "backdrop-saturate", + "sepia", + "backdrop-sepia", + "drop-shadow", + "backdrop-opacity", + // Transition utilities + "transition", + "delay", + "duration", + "ease", + // Will change + "will-change", + // Content + "content", + // Contain + "contain", + // Line height + "leading", + // Letter spacing + "tracking", + // Outline utilities + "outline", + "outline-offset", + // Opacity + "opacity", + // Text underline offset + "underline-offset", + // Text utilities + "text", + // Text shadow + "text-shadow", + // Box shadow + "shadow", + "inset-shadow", + // Ring utilities + "ring", + "inset-ring", + "ring-offset", + // Container queries + "@container", + // Color utilities + "accent", + "caret", + "divide", + "placeholder", + // Negative variants (prefixed with -) + "-inset", + "-inset-x", + "-inset-y", + "-start", + "-end", + "-top", + "-right", + "-bottom", + "-left", + "-z", + "-order", + "-m", + "-mx", + "-my", + "-ms", + "-me", + "-mt", + "-mr", + "-mb", + "-ml", + "-translate", + "-translate-x", + "-translate-y", + "-translate-z", + "-scale", + "-scale-x", + "-scale-y", + "-scale-z", + "-rotate", + "-rotate-x", + "-rotate-y", + "-rotate-z", + "-skew", + "-skew-x", + "-skew-y", + "-space-x", + "-space-y", + "-scroll-m", + "-scroll-mx", + "-scroll-my", + "-scroll-ms", + "-scroll-me", + "-scroll-mt", + "-scroll-mr", + "-scroll-mb", + "-scroll-ml", + "-indent", + "-tracking", + "-hue-rotate", + "-backdrop-hue-rotate", + "-outline-offset", + "-underline-offset", + "-bg-linear", + "-bg-conic", + "-mask-linear", + "-mask-conic", +]; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn must_be_sorted() { + BASENAMES_WITH_DASHES.windows(2).for_each(|pair| { + assert!( + pair[0] <= pair[1], + "Expected {} to be less than or equal to {}", + pair[0], + pair[1] + ) + }); + } +}