Skip to content

Commit 5aee292

Browse files
authored
Merge pull request #777 from dev-five-git/fix/ts-styled-output
fix(extractor): compile styled components in non-JSX source modules
2 parents 1430cc2 + b853a64 commit 5aee292

11 files changed

Lines changed: 876 additions & 81 deletions

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"changes": {
3+
"bindings/devup-ui-wasm/package.json": "Patch"
4+
},
5+
"note": "Generate React JSX-runtime calls for styled components in non-JSX source modules while preserving JSX and TSX output. No new build errors.",
6+
"date": "2026-10-13T00:00:00.000Z"
7+
}

‎libs/extractor/src/extractor/extract_style_from_styled.rs‎

Lines changed: 93 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use super::styled_jsx::StyledJsx;
12
use rustc_hash::FxHashMap;
23

34
use crate::{
@@ -44,6 +45,7 @@ const STYLED_FACTORY: &str =
4445
/// only the runtime gives the component
4546
struct Base<'a> {
4647
name: String,
48+
target: Option<Expression<'a>>,
4749
styles: Option<Vec<ExtractStyleValue>>,
4850
bound: Option<Expression<'a>>,
4951
}
@@ -52,6 +54,7 @@ impl<'a> Base<'a> {
5254
const fn named(name: String) -> Self {
5355
Self {
5456
name,
57+
target: None,
5558
styles: None,
5659
bound: None,
5760
}
@@ -113,10 +116,14 @@ fn tag_from_argument<'a>(
113116
}
114117
Some(jsx_name(argument).map_or_else(
115118
|| Base {
119+
target: Some(Expression::new_identifier(SPAN, STYLED_BASE, ast_builder)),
116120
bound: Some(argument.clone_in(ast_builder.allocator())),
117121
..Base::named(STYLED_BASE.to_string())
118122
},
119-
Base::named,
123+
|name| Base {
124+
target: Some(argument.clone_in(ast_builder.allocator())),
125+
..Base::named(name)
126+
},
120127
))
121128
}
122129

@@ -185,6 +192,7 @@ pub fn extract_style_from_styled<'a>(
185192
split_filename: Option<&str>,
186193
imports: &FxHashMap<String, ExportVariableKind>,
187194
attrs: &[Expression<'a>],
195+
helper: &mut Option<StyledJsx>,
188196
) -> (ExtractResult<'a>, Expression<'a>, Vec<(u32, String)>) {
189197
let original_scope = expression.clone_in(ast_builder.allocator());
190198
let mut composed_classes = Vec::new();
@@ -286,12 +294,13 @@ pub fn extract_style_from_styled<'a>(
286294
));
287295
let component = create_styled_component(
288296
ast_builder,
289-
&base.name,
297+
&base,
290298
StyledRender {
291299
class_name: &class_name,
292300
style_vars: &gen_styles(ast_builder, &props_styles, None),
293301
bindings: Vec::new(),
294302
},
303+
helper,
295304
);
296305
let styled_component = base.render(ast_builder, apply_attrs(ast_builder, component, attrs));
297306

@@ -363,12 +372,13 @@ pub fn extract_style_from_styled<'a>(
363372
);
364373
let component = create_styled_component(
365374
ast_builder,
366-
&base.name,
375+
&base,
367376
StyledRender {
368377
class_name: &class_name,
369378
style_vars: &gen_styles(ast_builder, &styles, None),
370379
bindings,
371380
},
381+
helper,
372382
);
373383
let styled_component = base.render(ast_builder, apply_attrs(ast_builder, component, attrs));
374384

@@ -575,8 +585,9 @@ struct StyledRender<'b, 'a> {
575585

576586
fn create_styled_component<'a>(
577587
ast_builder: &AstBuilder<'a>,
578-
tag_name: &str,
588+
base: &Base<'a>,
579589
render: StyledRender<'_, 'a>,
590+
helper: &mut Option<StyledJsx>,
580591
) -> Expression<'a> {
581592
let StyledRender {
582593
class_name,
@@ -637,92 +648,93 @@ fn create_styled_component<'a>(
637648
None::<oxc_allocator::Box<oxc_ast::ast::FormalParameterRest<'a>>>,
638649
ast_builder,
639650
);
640-
let body = Expression::new_jsx_element(
641-
SPAN,
642-
JSXOpeningElement::boxed(
651+
let class_name = class_name.as_ref().map_or_else(
652+
|| Expression::new_identifier(SPAN, "className", ast_builder),
653+
|name| {
654+
wrap_array_filter(
655+
ast_builder,
656+
&[
657+
name.clone_in(ast_builder.allocator()),
658+
Expression::new_identifier(SPAN, "className", ast_builder),
659+
],
660+
)
661+
.unwrap_or_else(|| name.clone_in(ast_builder.allocator()))
662+
},
663+
);
664+
let style = style_vars.as_ref().map_or_else(
665+
|| Expression::new_identifier(SPAN, "style", ast_builder),
666+
|style_vars| {
667+
merge_object_expressions(
668+
ast_builder,
669+
&[
670+
style_vars.clone_in(ast_builder.allocator()),
671+
Expression::new_identifier(SPAN, "style", ast_builder),
672+
],
673+
)
674+
.unwrap_or_else(|| style_vars.clone_in(ast_builder.allocator()))
675+
},
676+
);
677+
let body = if let Some(helper) = helper {
678+
let target = base.target.as_ref().map_or_else(
679+
|| {
680+
Expression::new_string_literal(
681+
SPAN,
682+
Str::from_in(&base.name, ast_builder.allocator()),
683+
None,
684+
ast_builder,
685+
)
686+
},
687+
|target| target.clone_in(ast_builder.allocator()),
688+
);
689+
helper.render(ast_builder, target, (class_name, style))
690+
} else {
691+
Expression::new_jsx_element(
643692
SPAN,
644-
JSXElementName::new_identifier(
693+
JSXOpeningElement::boxed(
645694
SPAN,
646-
Str::from_in(tag_name, ast_builder.allocator()),
647-
ast_builder,
648-
),
649-
None::<oxc_allocator::Box<oxc_ast::ast::TSTypeParameterInstantiation<'a>>>,
650-
oxc_allocator::Vec::from_iter_in(
651-
vec![
652-
JSXAttributeItem::new_spread_attribute(
653-
SPAN,
654-
Expression::new_identifier(SPAN, "rest", ast_builder),
655-
ast_builder,
656-
),
657-
JSXAttributeItem::new_attribute(
658-
SPAN,
659-
JSXAttributeName::new_identifier(SPAN, "className", ast_builder),
660-
Some(JSXAttributeValue::new_expression_container(
695+
JSXElementName::new_identifier(
696+
SPAN,
697+
Str::from_in(&base.name, ast_builder.allocator()),
698+
ast_builder,
699+
),
700+
None::<oxc_allocator::Box<oxc_ast::ast::TSTypeParameterInstantiation<'a>>>,
701+
oxc_allocator::Vec::from_iter_in(
702+
vec![
703+
JSXAttributeItem::new_spread_attribute(
661704
SPAN,
662-
class_name
663-
.as_ref()
664-
.map_or_else(
665-
|| Expression::new_identifier(SPAN, "className", ast_builder),
666-
|name| {
667-
wrap_array_filter(
668-
ast_builder,
669-
&[
670-
name.clone_in(ast_builder.allocator()),
671-
Expression::new_identifier(
672-
SPAN,
673-
"className",
674-
ast_builder,
675-
),
676-
],
677-
)
678-
.unwrap_or_else(|| name.clone_in(ast_builder.allocator()))
679-
},
680-
)
681-
.into(),
705+
Expression::new_identifier(SPAN, "rest", ast_builder),
682706
ast_builder,
683-
)),
684-
ast_builder,
685-
),
686-
JSXAttributeItem::new_attribute(
687-
SPAN,
688-
JSXAttributeName::new_identifier(SPAN, "style", ast_builder),
689-
Some(JSXAttributeValue::new_expression_container(
707+
),
708+
JSXAttributeItem::new_attribute(
690709
SPAN,
691-
style_vars
692-
.as_ref()
693-
.map_or_else(
694-
|| Expression::new_identifier(SPAN, "style", ast_builder),
695-
|style_vars| {
696-
merge_object_expressions(
697-
ast_builder,
698-
&[
699-
style_vars.clone_in(ast_builder.allocator()),
700-
Expression::new_identifier(
701-
SPAN,
702-
"style",
703-
ast_builder,
704-
),
705-
],
706-
)
707-
.unwrap_or_else(
708-
|| style_vars.clone_in(ast_builder.allocator()),
709-
)
710-
},
711-
)
712-
.into(),
710+
JSXAttributeName::new_identifier(SPAN, "className", ast_builder),
711+
Some(JSXAttributeValue::new_expression_container(
712+
SPAN,
713+
class_name.into(),
714+
ast_builder,
715+
)),
713716
ast_builder,
714-
)),
715-
ast_builder,
716-
),
717-
],
717+
),
718+
JSXAttributeItem::new_attribute(
719+
SPAN,
720+
JSXAttributeName::new_identifier(SPAN, "style", ast_builder),
721+
Some(JSXAttributeValue::new_expression_container(
722+
SPAN,
723+
style.into(),
724+
ast_builder,
725+
)),
726+
ast_builder,
727+
),
728+
],
729+
ast_builder,
730+
),
718731
ast_builder,
719732
),
733+
oxc_allocator::Vec::new_in(ast_builder),
734+
None::<oxc_allocator::Box<oxc_ast::ast::JSXClosingElement<'a>>>,
720735
ast_builder,
721-
),
722-
oxc_allocator::Vec::new_in(ast_builder),
723-
None::<oxc_allocator::Box<oxc_ast::ast::JSXClosingElement<'a>>>,
724-
ast_builder,
725-
);
736+
)
737+
};
726738
Expression::new_arrow_function_expression(
727739
SPAN,
728740
false,

‎libs/extractor/src/extractor/mod.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ pub(super) mod extract_style_from_jsx;
99
pub(super) mod extract_style_from_member_expression;
1010
pub(super) mod extract_style_from_styled;
1111
pub(super) mod extract_style_from_stylex;
12+
pub(super) mod styled_jsx;
1213
mod styled_record;
1314
mod styled_record_evaluation;
1415
#[cfg(test)]

0 commit comments

Comments
 (0)