Skip to content

Commit 7ce9bb7

Browse files
authored
Merge pull request #220 from dev-five-git/support-auto-insert-to-props
Support auto insert to props
2 parents ea6fe0b + 940cd56 commit 7ce9bb7

File tree

87 files changed

+169
-665
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

87 files changed

+169
-665
lines changed

.changeset/eager-singers-cover.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@devup-ui/wasm": patch
3+
---
4+
5+
Feat auto insertion className, style props

libs/extractor/src/prop_modify_utils.rs

Lines changed: 96 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ use oxc_ast::AstBuilder;
66
use oxc_ast::ast::JSXAttributeItem::Attribute;
77
use oxc_ast::ast::JSXAttributeName::Identifier;
88
use oxc_ast::ast::{
9-
Expression, JSXAttributeItem, JSXAttributeValue, JSXExpression, ObjectPropertyKind,
10-
PropertyKey, PropertyKind, TemplateElementValue,
9+
Expression, JSXAttributeItem, JSXAttributeValue, JSXExpression, LogicalOperator,
10+
ObjectPropertyKind, PropertyKey, PropertyKind, TemplateElementValue,
1111
};
1212
use oxc_span::SPAN;
1313

@@ -21,6 +21,7 @@ pub fn modify_prop_object<'a>(
2121
) {
2222
let mut class_name_prop = None;
2323
let mut style_prop = None;
24+
let mut spread_props = vec![];
2425
for idx in (0..props.len()).rev() {
2526
let prop = props.remove(idx);
2627
match prop {
@@ -40,14 +41,20 @@ pub fn modify_prop_object<'a>(
4041
}
4142
props.insert(idx, ObjectPropertyKind::ObjectProperty(attr));
4243
}
43-
_ => {
44-
props.insert(idx, prop);
44+
ObjectPropertyKind::SpreadProperty(spread) => {
45+
spread_props.push(spread.argument.clone_in(ast_builder.allocator));
46+
props.insert(idx, ObjectPropertyKind::SpreadProperty(spread));
4547
}
4648
}
4749
}
4850

49-
if let Some(ex) = get_class_name_expression(ast_builder, &class_name_prop, styles, style_order)
50-
{
51+
if let Some(ex) = get_class_name_expression(
52+
ast_builder,
53+
&class_name_prop,
54+
styles,
55+
style_order,
56+
&spread_props,
57+
) {
5158
props.push(ObjectPropertyKind::ObjectProperty(
5259
ast_builder.alloc_object_property(
5360
SPAN,
@@ -60,7 +67,9 @@ pub fn modify_prop_object<'a>(
6067
),
6168
));
6269
}
63-
if let Some(ex) = get_style_expression(ast_builder, &style_prop, styles, &style_vars) {
70+
if let Some(ex) =
71+
get_style_expression(ast_builder, &style_prop, styles, &style_vars, &spread_props)
72+
{
6473
props.push(ObjectPropertyKind::ObjectProperty(
6574
ast_builder.alloc_object_property(
6675
SPAN,
@@ -84,6 +93,7 @@ pub fn modify_props<'a>(
8493
) {
8594
let mut class_name_prop = None;
8695
let mut style_prop = None;
96+
let mut spread_props = vec![];
8797
for idx in (0..props.len()).rev() {
8898
let prop = props.remove(idx);
8999
match prop {
@@ -125,13 +135,19 @@ pub fn modify_props<'a>(
125135
}
126136
props.insert(idx, Attribute(attr));
127137
}
128-
_ => {
129-
props.insert(idx, prop);
138+
JSXAttributeItem::SpreadAttribute(spread) => {
139+
spread_props.push(spread.argument.clone_in(ast_builder.allocator));
140+
props.insert(idx, JSXAttributeItem::SpreadAttribute(spread));
130141
}
131142
}
132143
}
133-
if let Some(ex) = get_class_name_expression(ast_builder, &class_name_prop, styles, style_order)
134-
{
144+
if let Some(ex) = get_class_name_expression(
145+
ast_builder,
146+
&class_name_prop,
147+
styles,
148+
style_order,
149+
&spread_props,
150+
) {
135151
props.push(Attribute(ast_builder.alloc_jsx_attribute(
136152
SPAN,
137153
Identifier(ast_builder.alloc_jsx_identifier(SPAN, "className")),
@@ -144,7 +160,9 @@ pub fn modify_props<'a>(
144160
}),
145161
)));
146162
}
147-
if let Some(ex) = get_style_expression(ast_builder, &style_prop, styles, &style_vars) {
163+
if let Some(ex) =
164+
get_style_expression(ast_builder, &style_prop, styles, &style_vars, &spread_props)
165+
{
148166
props.push(Attribute(ast_builder.alloc_jsx_attribute(
149167
SPAN,
150168
Identifier(ast_builder.alloc_jsx_identifier(SPAN, "style")),
@@ -160,16 +178,30 @@ pub fn get_class_name_expression<'a>(
160178
class_name_prop: &Option<Expression<'a>>,
161179
styles: &mut [ExtractStyleProp<'a>],
162180
style_order: Option<u8>,
181+
spread_props: &[Expression<'a>],
163182
) -> Option<Expression<'a>> {
164183
// should modify class name prop
165184
merge_string_expressions(
166185
ast_builder,
167186
[
168-
class_name_prop.clone_in(ast_builder.allocator),
187+
class_name_prop
188+
.as_ref()
189+
.map(|class_name| convert_class_name(ast_builder, class_name)),
169190
gen_class_names(ast_builder, styles, style_order),
170191
]
171192
.into_iter()
172193
.flatten()
194+
.chain(spread_props.iter().map(|ex| {
195+
convert_class_name(
196+
ast_builder,
197+
&Expression::StaticMemberExpression(ast_builder.alloc_static_member_expression(
198+
SPAN,
199+
ex.clone_in(ast_builder.allocator),
200+
ast_builder.identifier_name(SPAN, ast_builder.atom("className")),
201+
true,
202+
)),
203+
)
204+
}))
173205
.collect::<Vec<_>>()
174206
.as_slice(),
175207
)
@@ -180,6 +212,7 @@ pub fn get_style_expression<'a>(
180212
style_prop: &Option<Expression<'a>>,
181213
styles: &[ExtractStyleProp<'a>],
182214
style_vars: &Option<Expression<'a>>,
215+
spread_props: &[Expression<'a>],
183216
) -> Option<Expression<'a>> {
184217
merge_object_expressions(
185218
ast_builder,
@@ -192,6 +225,14 @@ pub fn get_style_expression<'a>(
192225
]
193226
.into_iter()
194227
.flatten()
228+
.chain(spread_props.iter().map(|ex| {
229+
Expression::StaticMemberExpression(ast_builder.alloc_static_member_expression(
230+
SPAN,
231+
ex.clone_in(ast_builder.allocator),
232+
ast_builder.identifier_name(SPAN, ast_builder.atom("style")),
233+
true,
234+
))
235+
}))
195236
.collect::<Vec<_>>()
196237
.as_slice(),
197238
)
@@ -210,9 +251,14 @@ fn merge_string_expressions<'a>(
210251

211252
let mut string_literals: std::vec::Vec<String> = vec![];
212253
let mut other_expressions = vec![];
254+
let mut prev_str = false;
213255
for ex in expressions {
214-
string_literals.push("".to_string());
256+
if !prev_str {
257+
string_literals.push("".to_string());
258+
prev_str = false;
259+
}
215260
if let Expression::StringLiteral(literal) = ex {
261+
prev_str = true;
216262
if !string_literals.is_empty() {
217263
string_literals
218264
.last_mut()
@@ -273,12 +319,14 @@ fn merge_string_expressions<'a>(
273319
let trimmed = s.trim();
274320
if trimmed.is_empty() {
275321
"".to_string()
276-
} else if idx > 0 && idx == string_literals.len() - 1 {
277-
if string_literals.len() == other_expressions.len() {
278-
format!(" {trimmed} ")
322+
} else if idx == string_literals.len() - 1 {
323+
let prefix = if idx == 0 { "" } else { " " };
324+
let suffix = if string_literals.len() == other_expressions.len() {
325+
" "
279326
} else {
280-
format!(" {trimmed}")
281-
}
327+
""
328+
};
329+
format!("{prefix}{trimmed}{suffix}")
282330
} else if idx == string_literals.len() - 1 {
283331
trimmed.to_string()
284332
} else {
@@ -333,6 +381,35 @@ fn merge_object_expressions<'a>(
333381
))
334382
}
335383

384+
pub fn convert_class_name<'a>(
385+
ast_builder: &AstBuilder<'a>,
386+
class_name: &Expression<'a>,
387+
) -> Expression<'a> {
388+
if matches!(
389+
class_name,
390+
Expression::StringLiteral(_)
391+
| Expression::TemplateLiteral(_)
392+
| Expression::NumericLiteral(_)
393+
) {
394+
return class_name.clone_in(ast_builder.allocator);
395+
}
396+
397+
// wrap ( and ?? ''
398+
Expression::LogicalExpression(
399+
ast_builder.alloc_logical_expression(
400+
SPAN,
401+
Expression::ParenthesizedExpression(
402+
ast_builder.alloc_parenthesized_expression(
403+
SPAN,
404+
class_name.clone_in(ast_builder.allocator),
405+
),
406+
),
407+
LogicalOperator::Coalesce,
408+
Expression::StringLiteral(ast_builder.alloc_string_literal(SPAN, "", None)),
409+
),
410+
)
411+
}
412+
336413
pub fn convert_style_vars<'a>(
337414
ast_builder: &AstBuilder<'a>,
338415
style_vars: &Expression<'a>,

libs/extractor/src/snapshots/extractor__tests__avoid_same_name_component.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
source: libs/extractor/src/lib.rs
3-
expression: "ToBTreeSet::from(extract(\"test.js\",\nr#\"import {Box} from '@devup-ui/core'\nimport {Button} from '@devup/ui'\n ;<Box bg=\"red\" background=\"red\" />\n ;<Button bg=\"red\" background=\"red\" />\n \"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap())"
3+
expression: "ToBTreeSet::from(extract(\"test.jsx\",\nr#\"import {Box} from '@devup-ui/core'\nimport {Button} from '@devup/ui'\n ;<Box bg=\"red\" background=\"red\" />\n ;<Button bg=\"red\" background=\"red\" />\n \"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap())"
44
---
55
ToBTreeSet {
66
styles: {

libs/extractor/src/snapshots/extractor__tests__backtick_prop-2.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
source: libs/extractor/src/lib.rs
3-
expression: "ToBTreeSet::from(extract(\"test.js\",\nr#\"import {Box} from '@devup-ui/core'\n <Box bg={`${variable}`} />\n \"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap())"
3+
expression: "ToBTreeSet::from(extract(\"test.jsx\",\nr#\"import {Box} from '@devup-ui/core'\n <Box bg={`${variable}`} />\n \"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap())"
44
---
55
ToBTreeSet {
66
styles: {

libs/extractor/src/snapshots/extractor__tests__backtick_prop.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
source: libs/extractor/src/lib.rs
3-
expression: "ToBTreeSet::from(extract(\"test.js\",\nr#\"import {Box} from '@devup-ui/core'\n <Box bg={`black`} />\n \"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap())"
3+
expression: "ToBTreeSet::from(extract(\"test.jsx\",\nr#\"import {Box} from '@devup-ui/core'\n <Box bg={`black`} />\n \"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap())"
44
---
55
ToBTreeSet {
66
styles: {

libs/extractor/src/snapshots/extractor__tests__component_in_func.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
source: libs/extractor/src/lib.rs
3-
expression: "ToBTreeSet::from(extract(\"test.js\",\nr#\"import {Flex} from '@devup-ui/core'\nPROCESS_DATA.map(({ id, title, content }, idx) => (\n <MotionDiv key={idx}>\n <Flex alignItems=\"center\" gap={[3, null, 5, null, 10]}>\n </Flex>\n </MotionDiv>\n ))\n \"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap())"
3+
expression: "ToBTreeSet::from(extract(\"test.jsx\",\nr#\"import {Flex} from '@devup-ui/core'\nPROCESS_DATA.map(({ id, title, content }, idx) => (\n <MotionDiv key={idx}>\n <Flex alignItems=\"center\" gap={[3, null, 5, null, 10]}>\n </Flex>\n </MotionDiv>\n ))\n \"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap())"
44
---
55
ToBTreeSet {
66
styles: {

libs/extractor/src/snapshots/extractor__tests__css_props_destructuring_assignment-2.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
source: libs/extractor/src/lib.rs
3-
expression: "ToBTreeSet::from(extract(\"test.js\",\nr#\"import {css} from '@devup-ui/core'\n <div className={css({\n ...(a ? { bg: 'red', border: \"solid 1px red\" } : { bg: 'blue' }),\n ...({ p: 1,m: 1 }),\n })} />\n \"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap())"
3+
expression: "ToBTreeSet::from(extract(\"test.jsx\",\nr#\"import {css} from '@devup-ui/core'\n <div className={css({\n ...(a ? { bg: 'red', border: \"solid 1px red\" } : { bg: 'blue' }),\n ...({ p: 1,m: 1 }),\n })} />\n \"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap())"
44
---
55
ToBTreeSet {
66
styles: {

libs/extractor/src/snapshots/extractor__tests__css_props_destructuring_assignment.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
source: libs/extractor/src/lib.rs
3-
expression: "ToBTreeSet::from(extract(\"test.js\",\nr#\"import {css} from '@devup-ui/core'\n <div className={css({\n ...(a ? { bg: 'red' } : { bg: 'blue' }),\n ...({ p: 1 }),\n })} />\n \"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap())"
3+
expression: "ToBTreeSet::from(extract(\"test.jsx\",\nr#\"import {css} from '@devup-ui/core'\n <div className={css({\n ...(a ? { bg: 'red' } : { bg: 'blue' }),\n ...({ p: 1 }),\n })} />\n \"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap())"
44
---
55
ToBTreeSet {
66
styles: {

libs/extractor/src/snapshots/extractor__tests__custom_selector-2.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
source: libs/extractor/src/lib.rs
3-
expression: "ToBTreeSet::from(extract(\"test.js\",\nr#\"import {Box} from '@devup-ui/core'\n <Box selectors={{\n \"*[aria-diabled='true'] &:hover\": {\n opacity: 0.5\n }\n }} />\n \"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap())"
3+
expression: "ToBTreeSet::from(extract(\"test.jsx\",\nr#\"import {Box} from '@devup-ui/core'\n <Box selectors={{\n \"*[aria-diabled='true'] &:hover\": {\n opacity: 0.5\n }\n }} />\n \"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap())"
44
---
55
ToBTreeSet {
66
styles: {

libs/extractor/src/snapshots/extractor__tests__custom_selector-3.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
source: libs/extractor/src/lib.rs
3-
expression: "ToBTreeSet::from(extract(\"test.js\",\nr#\"import {Box} from '@devup-ui/core'\n <Box selectors={{\n \"*[aria-diabled='true'] &\": {\n opacity: 0.5\n }\n }} />\n \"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap())"
3+
expression: "ToBTreeSet::from(extract(\"test.jsx\",\nr#\"import {Box} from '@devup-ui/core'\n <Box selectors={{\n \"*[aria-diabled='true'] &\": {\n opacity: 0.5\n }\n }} />\n \"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap())"
44
---
55
ToBTreeSet {
66
styles: {

0 commit comments

Comments
 (0)