Skip to content

Commit 3755926

Browse files
committed
Support conditional style order
1 parent f90ca2b commit 3755926

17 files changed

Lines changed: 1238 additions & 63 deletions
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"changes":{"packages/plugin-utils/package.json":"Patch","bindings/devup-ui-wasm/package.json":"Patch","packages/webpack-plugin/package.json":"Patch","packages/components/package.json":"Patch","packages/rsbuild-plugin/package.json":"Patch","packages/vite-plugin/package.json":"Patch","packages/next-plugin/package.json":"Patch","packages/bun-plugin/package.json":"Patch","packages/reset-css/package.json":"Patch","packages/react/package.json":"Patch","packages/eslint-plugin/package.json":"Patch"},"note":"Support conditonal styleOrder","date":"2026-02-10T07:40:05.477045100Z"}

libs/extractor/src/lib.rs

Lines changed: 288 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ mod visit;
1515
use crate::extract_style::extract_style_value::ExtractStyleValue;
1616
use crate::visit::DevupVisitor;
1717
use css::file_map::get_file_num_by_filename;
18-
use oxc_allocator::Allocator;
18+
use oxc_allocator::{Allocator, CloneIn};
1919
use oxc_ast::ast::Expression;
2020
use oxc_ast_visit::VisitMut;
2121
use oxc_codegen::{Codegen, CodegenOptions};
@@ -57,7 +57,45 @@ pub enum ExtractStyleProp<'a> {
5757
},
5858
}
5959

60-
impl ExtractStyleProp<'_> {
60+
impl<'a> ExtractStyleProp<'a> {
61+
pub fn clone_in(&self, alloc: &'a Allocator) -> Self {
62+
match self {
63+
ExtractStyleProp::Static(v) => ExtractStyleProp::Static(v.clone()),
64+
ExtractStyleProp::StaticArray(arr) => {
65+
ExtractStyleProp::StaticArray(arr.iter().map(|s| s.clone_in(alloc)).collect())
66+
}
67+
ExtractStyleProp::Conditional {
68+
condition,
69+
consequent,
70+
alternate,
71+
} => ExtractStyleProp::Conditional {
72+
condition: condition.clone_in(alloc),
73+
consequent: consequent.as_ref().map(|c| Box::new(c.clone_in(alloc))),
74+
alternate: alternate.as_ref().map(|a| Box::new(a.clone_in(alloc))),
75+
},
76+
ExtractStyleProp::Enum { condition, map } => ExtractStyleProp::Enum {
77+
condition: condition.clone_in(alloc),
78+
map: map
79+
.iter()
80+
.map(|(k, v)| (k.clone(), v.iter().map(|s| s.clone_in(alloc)).collect()))
81+
.collect(),
82+
},
83+
ExtractStyleProp::Expression { styles, expression } => ExtractStyleProp::Expression {
84+
styles: styles.clone(),
85+
expression: expression.clone_in(alloc),
86+
},
87+
ExtractStyleProp::MemberExpression { map, expression } => {
88+
ExtractStyleProp::MemberExpression {
89+
map: map
90+
.iter()
91+
.map(|(k, v)| (k.clone(), Box::new(v.clone_in(alloc))))
92+
.collect(),
93+
expression: expression.clone_in(alloc),
94+
}
95+
}
96+
}
97+
}
98+
6199
pub fn extract(&self) -> Vec<ExtractStyleValue> {
62100
match self {
63101
ExtractStyleProp::Static(style) => vec![style.clone()],
@@ -6202,6 +6240,254 @@ export {
62026240
));
62036241
}
62046242

6243+
#[test]
6244+
#[serial]
6245+
fn style_order_conditional() {
6246+
// Test 1: styleOrder={condition ? 5 : 10} — ternary with two static numbers
6247+
// Since jsx_expression_to_number doesn't handle ConditionalExpression,
6248+
// styleOrder should be None (not applied)
6249+
reset_class_map();
6250+
reset_file_map();
6251+
assert_debug_snapshot!(ToBTreeSet::from(
6252+
extract(
6253+
"test.jsx",
6254+
r#"import {Box} from '@devup-ui/core'
6255+
<Box styleOrder={isActive ? 5 : 10} bg="red" p="4" />
6256+
"#,
6257+
ExtractOption {
6258+
package: "@devup-ui/core".to_string(),
6259+
css_dir: "@devup-ui/core".to_string(),
6260+
single_css: true,
6261+
import_main_css: false,
6262+
import_aliases: HashMap::new()
6263+
}
6264+
)
6265+
.unwrap()
6266+
));
6267+
6268+
// Test 2: styleOrder={condition ? 5 : variable} — ternary with mixed static/dynamic
6269+
reset_class_map();
6270+
reset_file_map();
6271+
assert_debug_snapshot!(ToBTreeSet::from(
6272+
extract(
6273+
"test.jsx",
6274+
r#"import {Box} from '@devup-ui/core'
6275+
<Box styleOrder={isActive ? 5 : order} bg="red" p="4" />
6276+
"#,
6277+
ExtractOption {
6278+
package: "@devup-ui/core".to_string(),
6279+
css_dir: "@devup-ui/core".to_string(),
6280+
single_css: true,
6281+
import_main_css: false,
6282+
import_aliases: HashMap::new()
6283+
}
6284+
)
6285+
.unwrap()
6286+
));
6287+
6288+
// Test 3: styleOrder={variable} — fully dynamic styleOrder
6289+
reset_class_map();
6290+
reset_file_map();
6291+
assert_debug_snapshot!(ToBTreeSet::from(
6292+
extract(
6293+
"test.jsx",
6294+
r#"import {Box} from '@devup-ui/core'
6295+
<Box styleOrder={order} bg="red" p="4" />
6296+
"#,
6297+
ExtractOption {
6298+
package: "@devup-ui/core".to_string(),
6299+
css_dir: "@devup-ui/core".to_string(),
6300+
single_css: true,
6301+
import_main_css: false,
6302+
import_aliases: HashMap::new()
6303+
}
6304+
)
6305+
.unwrap()
6306+
));
6307+
6308+
// Test 4: styleOrder={condition ? 5 : 10} with conditional style props
6309+
// Verifies interaction between conditional styleOrder and conditional style values
6310+
reset_class_map();
6311+
reset_file_map();
6312+
assert_debug_snapshot!(ToBTreeSet::from(
6313+
extract(
6314+
"test.jsx",
6315+
r#"import {Box} from '@devup-ui/core'
6316+
<Box styleOrder={isActive ? 5 : 10} bg={isActive ? "red" : "blue"} />
6317+
"#,
6318+
ExtractOption {
6319+
package: "@devup-ui/core".to_string(),
6320+
css_dir: "@devup-ui/core".to_string(),
6321+
single_css: true,
6322+
import_main_css: false,
6323+
import_aliases: HashMap::new()
6324+
}
6325+
)
6326+
.unwrap()
6327+
));
6328+
6329+
// Test 5: styleOrder={condition ? 5 : 10} with css() className
6330+
reset_class_map();
6331+
reset_file_map();
6332+
assert_debug_snapshot!(ToBTreeSet::from(
6333+
extract(
6334+
"test.jsx",
6335+
r#"import {Box, css} from '@devup-ui/core'
6336+
<Box styleOrder={isActive ? 5 : 10} className={css({color:"white"})} bg="red" />
6337+
"#,
6338+
ExtractOption {
6339+
package: "@devup-ui/core".to_string(),
6340+
css_dir: "@devup-ui/core".to_string(),
6341+
single_css: true,
6342+
import_main_css: false,
6343+
import_aliases: HashMap::new()
6344+
}
6345+
)
6346+
.unwrap()
6347+
));
6348+
}
6349+
6350+
#[test]
6351+
#[serial]
6352+
fn style_order_logical_and() {
6353+
// Test 1: JSX path — styleOrder={a === 1 && 5}
6354+
// truthy → styleOrder=5, falsy → no styleOrder (None)
6355+
reset_class_map();
6356+
reset_file_map();
6357+
assert_debug_snapshot!(ToBTreeSet::from(
6358+
extract(
6359+
"test.jsx",
6360+
r#"import {Box} from '@devup-ui/core'
6361+
<Box styleOrder={a === 1 && 5} bg="red" p="4" />
6362+
"#,
6363+
ExtractOption {
6364+
package: "@devup-ui/core".to_string(),
6365+
css_dir: "@devup-ui/core".to_string(),
6366+
single_css: true,
6367+
import_main_css: false,
6368+
import_aliases: HashMap::new()
6369+
}
6370+
)
6371+
.unwrap()
6372+
));
6373+
6374+
// Test 2: JSX path — styleOrder={a === 1 && 5} with conditional style props
6375+
reset_class_map();
6376+
reset_file_map();
6377+
assert_debug_snapshot!(ToBTreeSet::from(
6378+
extract(
6379+
"test.jsx",
6380+
r#"import {Box} from '@devup-ui/core'
6381+
<Box styleOrder={a === 1 && 5} bg={isActive ? "red" : "blue"} />
6382+
"#,
6383+
ExtractOption {
6384+
package: "@devup-ui/core".to_string(),
6385+
css_dir: "@devup-ui/core".to_string(),
6386+
single_css: true,
6387+
import_main_css: false,
6388+
import_aliases: HashMap::new()
6389+
}
6390+
)
6391+
.unwrap()
6392+
));
6393+
6394+
// Test 3: Call expression path — styleOrder: a === 1 && 5
6395+
reset_class_map();
6396+
reset_file_map();
6397+
assert_debug_snapshot!(ToBTreeSet::from(
6398+
extract(
6399+
"test.mjs",
6400+
r#"import { jsx as e } from "react/jsx-runtime";
6401+
import { Box as o } from "@devup-ui/react";
6402+
function c() {
6403+
return e(o, { styleOrder: a === 1 && 5, bg: "red", p: "4" });
6404+
}
6405+
export { c as Lib };"#,
6406+
ExtractOption {
6407+
package: "@devup-ui/react".to_string(),
6408+
css_dir: "@devup-ui/react".to_string(),
6409+
single_css: true,
6410+
import_main_css: false,
6411+
import_aliases: HashMap::new()
6412+
}
6413+
)
6414+
.unwrap()
6415+
));
6416+
}
6417+
6418+
#[test]
6419+
#[serial]
6420+
fn style_order_conditional_call_expression() {
6421+
// Test 1: styleOrder: isActive ? 5 : 10 — ternary with two static numbers via call expression
6422+
reset_class_map();
6423+
reset_file_map();
6424+
assert_debug_snapshot!(ToBTreeSet::from(
6425+
extract(
6426+
"test.mjs",
6427+
r#"import { jsx as e } from "react/jsx-runtime";
6428+
import { Box as o } from "@devup-ui/react";
6429+
function c() {
6430+
return e(o, { styleOrder: isActive ? 5 : 10, bg: "red", p: "4" });
6431+
}
6432+
export { c as Lib };"#,
6433+
ExtractOption {
6434+
package: "@devup-ui/react".to_string(),
6435+
css_dir: "@devup-ui/react".to_string(),
6436+
single_css: true,
6437+
import_main_css: false,
6438+
import_aliases: HashMap::new()
6439+
}
6440+
)
6441+
.unwrap()
6442+
));
6443+
6444+
// Test 2: styleOrder: isActive ? 5 : 10 with conditional style props via call expression
6445+
reset_class_map();
6446+
reset_file_map();
6447+
assert_debug_snapshot!(ToBTreeSet::from(
6448+
extract(
6449+
"test.mjs",
6450+
r#"import { jsx as e } from "react/jsx-runtime";
6451+
import { Box as o } from "@devup-ui/react";
6452+
function c() {
6453+
return e(o, { styleOrder: isActive ? 5 : 10, bg: isActive ? "red" : "blue" });
6454+
}
6455+
export { c as Lib };"#,
6456+
ExtractOption {
6457+
package: "@devup-ui/react".to_string(),
6458+
css_dir: "@devup-ui/react".to_string(),
6459+
single_css: true,
6460+
import_main_css: false,
6461+
import_aliases: HashMap::new()
6462+
}
6463+
)
6464+
.unwrap()
6465+
));
6466+
6467+
// Test 3: static styleOrder via call expression (backward compat)
6468+
reset_class_map();
6469+
reset_file_map();
6470+
assert_debug_snapshot!(ToBTreeSet::from(
6471+
extract(
6472+
"test.mjs",
6473+
r#"import { jsx as e } from "react/jsx-runtime";
6474+
import { Box as o } from "@devup-ui/react";
6475+
function c() {
6476+
return e(o, { styleOrder: 5, bg: "red", p: "4" });
6477+
}
6478+
export { c as Lib };"#,
6479+
ExtractOption {
6480+
package: "@devup-ui/react".to_string(),
6481+
css_dir: "@devup-ui/react".to_string(),
6482+
single_css: true,
6483+
import_main_css: false,
6484+
import_aliases: HashMap::new()
6485+
}
6486+
)
6487+
.unwrap()
6488+
));
6489+
}
6490+
62056491
#[test]
62066492
#[serial]
62076493
fn style_order2() {

0 commit comments

Comments
 (0)