Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 3 additions & 4 deletions apps/landing/src/app/(detail)/docs/build-errors/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -1902,15 +1902,14 @@ export function f() { const c = css; c = () => 1; return c({ color: "red" }); }

**Why:** A followed alias cannot be reassigned after the rewrite removes its declaration.

**Fix:** Keep the const alias unchanged and call it.
**Fix:** Call the imported css directly instead of creating a local runtime alias.

{/* probe: mutated-const-alias after */}

```tsx
import { css } from '@devup-ui/react'
export function f() {
const c = css
return c({ color: 'red' })
return css({ color: 'red' })
}
```

Expand Down Expand Up @@ -3926,7 +3925,7 @@ The `devup/no-runtime-read` rule reports compile-time bindings read outside supp
`<binding>` is read at runtime, where it does not exist: the build compiles it only where it is called or rendered.
```

```tsx
```tsx-error
import { css } from '@devup-ui/react'
consume(css)
```
Expand Down
7 changes: 4 additions & 3 deletions e2e/tailwind-hover-order.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ for (const singleCss of [true, false]) {
for (const fixture of cases) {
for (const [index, width] of [640, 1024].entries()) {
const label = 'props' in fixture ? 'explicit Devup guard: ' : ''
const devupColorIndex = 'props' in fixture ? 1 : 0
test(`${label}${fixture.classes}, ${width}px, singleCss=${singleCss}`, async ({
page,
}, testInfo) => {
Expand Down Expand Up @@ -191,7 +192,7 @@ for (const singleCss of [true, false]) {
const observed = []
for (const [name, classes, css, color] of [
['Tailwind', fixture.classes, oracleCss, expected[0]],
['Devup', matched?.[1], devupCss, expected[1]],
['Devup', matched?.[1], devupCss, expected[devupColorIndex]],
]) {
await page.setContent(
`<!doctype html><html data-theme="dark"><head><style>${css}</style></head><body><button id="target" class="${classes}" ${disabled ? 'disabled' : ''} style="width:160px;height:80px">probe</button></body></html>`,
Expand All @@ -207,14 +208,14 @@ for (const singleCss of [true, false]) {
} else if (!disabled) {
await expect(target).toHaveCSS(
'background-color',
colors.red[name === 'Tailwind' ? 0 : 1],
colors.red[name === 'Tailwind' ? 0 : devupColorIndex],
)
}
if (fixture.classes.includes('focus:')) {
await target.focus()
await expect(target).toHaveCSS(
'background-color',
colors.green[name === 'Tailwind' ? 0 : 1],
colors.green[name === 'Tailwind' ? 0 : devupColorIndex],
)
}
await page.mouse.down()
Expand Down
6 changes: 6 additions & 0 deletions libs/extractor/src/tailwind_children.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ pub fn children_utility(name: &str) -> Option<Vec<Declaration>> {
#[allow(clippy::expect_used, clippy::unwrap_used)]
mod tests {
use rstest::rstest;
use serial_test::serial;

use super::*;
use crate::tailwind::{declarations_of, parse_class};
Expand Down Expand Up @@ -177,6 +178,7 @@ mod tests {
])]
#[case("space-x-reverse", &[("--tw-space-x-reverse", "1")])]
#[case("space-y-reverse", &[("--tw-space-y-reverse", "1")])]
#[serial]
fn space_adds_margins_between_the_children(
#[case] class: &str,
#[case] expected: &[(&str, &str)],
Expand Down Expand Up @@ -229,6 +231,7 @@ mod tests {
}

#[test]
#[serial]
fn divide_reverse_style_and_color() {
expect("divide-x-reverse", &[("--tw-divide-x-reverse", "1")]);
expect("divide-y-reverse", &[("--tw-divide-y-reverse", "1")]);
Expand Down Expand Up @@ -268,11 +271,13 @@ mod tests {
#[case("space-x-huge")]
#[case("-space-x-reverse")]
#[case("space-z-4")]
#[serial]
fn unknown_children_utilities_stay_as_written(#[case] class: &str) {
assert_eq!(declarations_of(class), None, "{class}");
}

#[test]
#[serial]
fn children_utilities_apply_under_variants_to_the_children() {
let class = parse_class("space-x-4").unwrap();
assert_eq!(class.conditions.len(), 1);
Expand Down Expand Up @@ -310,6 +315,7 @@ mod tests {
}

#[test]
#[serial]
fn children_rules_register_their_variables() {
assert_eq!(
parse_class("space-x-4").unwrap().rules(),
Expand Down
5 changes: 5 additions & 0 deletions libs/extractor/src/tailwind_color.rs
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,7 @@ mod tests {
#[case("caret-fuchsia-100", "oklch(95.2% 0.037 318.852)")]
#[case("fill-zinc-900", "oklch(21% 0.006 285.885)")]
#[case("stroke-stone-200", "oklch(92.3% 0.003 48.717)")]
#[serial]
fn palette_is_the_default_theme_of_v4(#[case] class: &str, #[case] expected: &str) {
assert_eq!(color(class).as_deref(), Some(expected));
}
Expand Down Expand Up @@ -474,6 +475,7 @@ mod tests {
)]
#[case("bg-black/10", "color-mix(in oklab, #000 10%, transparent)")]
#[case("text-white/75", "color-mix(in oklab, #fff 75%, transparent)")]
#[serial]
fn opacity_modifier_mixes_with_transparent(#[case] class: &str, #[case] expected: &str) {
assert_eq!(color(class).as_deref(), Some(expected));
}
Expand All @@ -489,6 +491,7 @@ mod tests {
#[case("bg-red-500/50/50")]
#[case("bg-red-501")]
#[case("bg-unknown")]
#[serial]
fn invalid_color_stays_as_written(#[case] class: &str) {
assert_eq!(declarations_of(class), None, "{class}");
}
Expand Down Expand Up @@ -541,6 +544,7 @@ mod tests {
("--tw-blur", "blur(8px) !important"),
("filter", "var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,) !important"),
])]
#[serial]
fn important_marks_every_declaration(#[case] class: &str, #[case] expected: &[(&str, &str)]) {
let expected: Vec<(String, String)> = expected
.iter()
Expand All @@ -550,6 +554,7 @@ mod tests {
}

#[test]
#[serial]
fn important_applies_under_variants() {
let class = crate::tailwind::parse_class("hover:p-4!").unwrap();
assert_eq!(class.declarations, vec![decl("padding", "1rem !important")]);
Expand Down
23 changes: 23 additions & 0 deletions libs/extractor/src/tailwind_effects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,7 @@ mod tests {
#[case("blur-none", " ")]
#[case("blur-[2px]", "blur(2px)")]
#[case("blur-(--radius)", "blur(var(--radius))")]
#[serial]
fn blur_composes_through_its_variable(#[case] class: &str, #[case] value: &str) {
assert_eq!(written(class), filter("--tw-blur", value));
}
Expand All @@ -729,6 +730,7 @@ mod tests {
#[case("saturate-150", "--tw-saturate", "saturate(150%)")]
#[case("sepia", "--tw-sepia", "sepia(100%)")]
#[case("sepia-50", "--tw-sepia", "sepia(50%)")]
#[serial]
fn filter_functions_set_their_own_variable(
#[case] class: &str,
#[case] variable: &str,
Expand Down Expand Up @@ -764,6 +766,7 @@ mod tests {
#[case("backdrop-opacity-12.5", "--tw-backdrop-opacity", "opacity(12.5%)")]
#[case("backdrop-saturate-200", "--tw-backdrop-saturate", "saturate(200%)")]
#[case("backdrop-sepia", "--tw-backdrop-sepia", "sepia(100%)")]
#[serial]
fn backdrop_filter_functions_compose_too(
#[case] class: &str,
#[case] variable: &str,
Expand All @@ -773,6 +776,7 @@ mod tests {
}

#[test]
#[serial]
fn filter_and_backdrop_filter_read_every_function() {
expect("filter", &[("filter", FILTER)]);
expect("filter-none", &[("filter", "none")]);
Expand Down Expand Up @@ -804,6 +808,7 @@ mod tests {
}

#[test]
#[serial]
fn two_filters_add_up_instead_of_overwriting() {
let blur = written("blur-sm");
let grayscale = written("grayscale");
Expand Down Expand Up @@ -852,6 +857,7 @@ mod tests {
#[case("-filter-none")]
#[case("backdrop-")]
#[case("blurry")]
#[serial]
fn unknown_filters_stay_as_written(#[case] class: &str) {
assert_eq!(declarations_of(class), None, "{class}");
}
Expand Down Expand Up @@ -884,6 +890,7 @@ mod tests {
"0 25px 50px -12px var(--tw-shadow-color, rgb(0 0 0 / 0.25))"
)]
#[case("shadow-none", "0 0 #0000")]
#[serial]
fn shadow_sizes_follow_the_default_theme(#[case] class: &str, #[case] shadow: &str) {
expect(
class,
Expand Down Expand Up @@ -927,11 +934,13 @@ mod tests {
"shadow-current",
"color-mix(in oklab, currentcolor var(--tw-shadow-alpha), transparent)"
)]
#[serial]
fn shadow_colors_set_the_color_the_shadows_read(#[case] class: &str, #[case] color: &str) {
expect(class, &[("--tw-shadow-color", color)]);
}

#[test]
#[serial]
fn shadow_inherit_and_unknown_shadows() {
expect("shadow-inherit", &[("--tw-shadow-color", "inherit")]);
for class in [
Expand Down Expand Up @@ -984,6 +993,7 @@ mod tests {
#[case("bg-linear-to-r/increasing", "to right in oklch increasing hue")]
#[case("bg-linear-to-r/decreasing", "to right in oklch decreasing hue")]
#[case("bg-linear-90/[in_hsl_longer_hue]", "90deg in hsl longer hue")]
#[serial]
fn linear_gradients_use_the_syntax_of_v4(#[case] class: &str, #[case] position: &str) {
expect(
class,
Expand All @@ -998,6 +1008,7 @@ mod tests {
}

#[test]
#[serial]
fn conic_and_radial_gradients() {
for (class, position, image) in [
(
Expand Down Expand Up @@ -1047,6 +1058,7 @@ mod tests {
}

#[test]
#[serial]
fn arbitrary_gradients_carry_their_own_stops() {
expect(
"bg-linear-[25deg,red_5%,yellow_60%,lime_90%,teal]",
Expand Down Expand Up @@ -1102,6 +1114,7 @@ mod tests {
#[case("bg-radial-45")]
#[case("bg-conic-x")]
#[case("bg-sweep-45")]
#[serial]
fn gradients_v4_does_not_define_stay_as_written(#[case] class: &str) {
assert_eq!(declarations_of(class), None, "{class}");
}
Expand All @@ -1110,6 +1123,7 @@ mod tests {
const VIA_STOPS: &str = "var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position)";

#[test]
#[serial]
fn gradient_stops_compose_through_variables() {
expect(
"from-red-500",
Expand Down Expand Up @@ -1157,6 +1171,7 @@ mod tests {
}

#[test]
#[serial]
fn gradient_stop_positions() {
expect("from-10%", &[("--tw-gradient-from-position", "10%")]);
expect("via-30%", &[("--tw-gradient-via-position", "30%")]);
Expand All @@ -1178,11 +1193,13 @@ mod tests {
#[case("-from-red-500")]
#[case("via-none/50")]
#[case("up-red-500")]
#[serial]
fn unknown_gradient_stops_stay_as_written(#[case] class: &str) {
assert_eq!(declarations_of(class), None, "{class}");
}

#[test]
#[serial]
fn gradient_rules_register_the_stop_variables() {
let rules = parse_class("from-red-500").unwrap().rules();
for name in [
Expand Down Expand Up @@ -1211,6 +1228,7 @@ mod tests {
}

#[test]
#[serial]
fn modifiers_and_variants_apply_to_composed_utilities() {
let class = parse_class("hover:blur-sm").unwrap();
assert_eq!(class.declarations, written_pairs("blur-sm"));
Expand All @@ -1237,6 +1255,7 @@ mod tests {
#[case("ring-2", "2px")]
#[case("ring-8", "8px")]
#[case("ring-[3px]", "3px")]
#[serial]
fn ring_widths_draw_the_ring_composed_with_the_shadows(
#[case] class: &str,
#[case] width: &str,
Expand All @@ -1256,6 +1275,7 @@ mod tests {
}

#[test]
#[serial]
fn ring_colors_inset_rings_and_offsets() {
expect("ring-inset", &[("--tw-ring-inset", "inset")]);
expect(
Expand Down Expand Up @@ -1327,6 +1347,7 @@ mod tests {
#[case("ring-offset-nothing")]
#[case("inset-ring-nothing")]
#[case("-ring-2")]
#[serial]
fn unknown_rings_stay_as_written(#[case] class: &str) {
assert_eq!(declarations_of(class), None, "{class}");
}
Expand All @@ -1338,6 +1359,7 @@ mod tests {
#[case("drop-shadow-lg", "0 4px 4px", "rgb(0 0 0 / 0.15)")]
#[case("drop-shadow-xl", "0 9px 7px", "rgb(0 0 0 / 0.1)")]
#[case("drop-shadow-2xl", "0 25px 25px", "rgb(0 0 0 / 0.15)")]
#[serial]
fn drop_shadow_sizes_compose_into_filter(
#[case] class: &str,
#[case] offsets: &str,
Expand All @@ -1359,6 +1381,7 @@ mod tests {
);
}
#[test]
#[serial]
fn drop_shadow_without_a_size_none_colors_and_arbitrary_values() {
expect(
"drop-shadow",
Expand Down
9 changes: 9 additions & 0 deletions libs/extractor/src/tailwind_misc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,7 @@ fn plain(name: &str) -> Option<Vec<Declaration>> {
#[allow(clippy::expect_used, clippy::unwrap_used)]
mod tests {
use rstest::rstest;
use serial_test::serial;

use crate::tailwind::declarations_of;

Expand Down Expand Up @@ -365,6 +366,7 @@ mod tests {
#[case("outline-double", &[("--tw-outline-style", "double"), ("outline-style", "double")])]
#[case("outline-none", &[("--tw-outline-style", "none"), ("outline-style", "none")])]
#[case("line-clamp-none", &[("overflow", "visible"), ("display", "block"), ("-webkit-box-orient", "horizontal"), ("-webkit-line-clamp", "unset")])]
#[serial]
fn fixed_utilities(#[case] class: &str, #[case] expected: &[(&str, &str)]) {
expect(class, expected);
}
Expand All @@ -373,6 +375,7 @@ mod tests {
#[case("line-clamp-3", "3")]
#[case("line-clamp-(--lines)", "var(--lines)")]
#[case("line-clamp-[calc(1+1)]", "calc(1 + 1)")]
#[serial]
fn line_clamp_clamps_to_a_number_of_lines(#[case] class: &str, #[case] lines: &str) {
expect(
class,
Expand All @@ -386,6 +389,7 @@ mod tests {
}

#[test]
#[serial]
fn text_decoration_offsets_and_outlines() {
expect("underline-offset-4", &[("text-underline-offset", "4px")]);
expect(
Expand Down Expand Up @@ -451,6 +455,7 @@ mod tests {
#[case("border-r-8", "border-right-style", "border-right-width", "8px")]
#[case("border-b-[2px]", "border-bottom-style", "border-bottom-width", "2px")]
#[case("border-l-2", "border-left-style", "border-left-width", "2px")]
#[serial]
fn border_widths_draw_in_the_border_style(
#[case] class: &str,
#[case] style: &str,
Expand All @@ -464,6 +469,7 @@ mod tests {
}

#[test]
#[serial]
fn border_styles_set_the_style_the_widths_draw_in() {
for style in ["solid", "dashed", "dotted", "double", "hidden", "none"] {
expect(
Expand All @@ -474,6 +480,7 @@ mod tests {
}

#[test]
#[serial]
fn border_spacing_sets_both_axes_unless_one_is_named() {
expect(
"border-spacing-2",
Expand Down Expand Up @@ -509,6 +516,7 @@ mod tests {
}

#[test]
#[serial]
fn logical_sides_scroll_spacing_and_indent() {
expect("start-4", &[("inset-inline-start", "1rem")]);
expect("end-0", &[("inset-inline-end", "0px")]);
Expand Down Expand Up @@ -574,6 +582,7 @@ mod tests {
#[case("-indent")]
#[case("-table-auto")]
#[case("antialiased-")]
#[serial]
fn unknown_utilities_stay_as_written(#[case] class: &str) {
assert_eq!(declarations_of(class), None, "{class}");
}
Expand Down
Loading
Loading