Skip to content

Commit

Permalink
dynamic attributes serialization
Browse files Browse the repository at this point in the history
  • Loading branch information
bartlomieju committed Oct 20, 2023
1 parent 1fe4137 commit d3b72c3
Showing 1 changed file with 81 additions and 34 deletions.
115 changes: 81 additions & 34 deletions src/transpiling/jsx_string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use swc_ecma_visit::{

struct JsxString {
next_index: usize,
templates: Vec<(usize, (Vec<String>, Vec<String>))>,
templates: Vec<(usize, Vec<String>)>,
}

impl Default for JsxString {
Expand All @@ -36,30 +36,29 @@ fn create_tpl_binding_name(index: usize) -> String {

fn serialize_jsx_element_to_string_vec(
el: JSXElement,
) -> (Vec<String>, Vec<String>) {
) -> (Vec<String>, Vec<Box<Expr>>) {
let name = match el.opening.name {
JSXElementName::Ident(ident) => ident.sym.to_string(),
_ => todo!(),
};


let mut strings: Vec<String> = vec![
String::from("<")
];
let mut dynamic_exprs: Vec<String> = vec![];
let mut strings: Vec<String> = vec![String::from("<")];
let mut dynamic_exprs: Vec<Box<Expr>> = vec![];

strings.last_mut().unwrap().push_str(name.as_str());

if !el.opening.attrs.is_empty() {
for attr in el.opening.attrs.iter() {
let mut is_dynamic = false;
let mut serialized_attr = String::new();
let mut name = "".to_string();
// <button class="btn">
match attr {
JSXAttrOrSpread::JSXAttr(jsx_attr) => {
match &jsx_attr.name {
JSXAttrName::Ident(ident) => {
serialized_attr.push_str(ident.sym.to_string().as_str());
name = ident.sym.to_string();
serialized_attr.push_str(name.as_str());
serialized_attr.push_str("=\"");
}
JSXAttrName::JSXNamespacedName(_) => todo!(),
Expand All @@ -83,6 +82,25 @@ fn serialize_jsx_element_to_string_vec(
strings.push("".to_string());
is_dynamic = true;
eprintln!("jsx_expr_container {:#?}", jsx_expr_container);
match &jsx_expr_container.expr {
JSXExpr::JSXEmptyExpr(_) => todo!(),
JSXExpr::Expr(expr) => {
let obj_expr = Box::new(Expr::Object(ObjectLit {
span: DUMMY_SP,
props: vec![PropOrSpread::Prop(Box::new(Prop::KeyValue(
KeyValueProp {
key: PropName::Str(Str {
span: DUMMY_SP,
value: name.into(),
raw: None,
}),
value: expr.clone(),
},
)))],
}));
dynamic_exprs.push(obj_expr);
}
}
}
JSXAttrValue::JSXElement(_) => todo!(),
JSXAttrValue::JSXFragment(_) => todo!(),
Expand All @@ -93,7 +111,10 @@ fn serialize_jsx_element_to_string_vec(
serialized_attr.push_str("\"");
if !is_dynamic {
strings.last_mut().unwrap().push_str(" ");
strings.last_mut().unwrap().push_str(&serialized_attr.as_str());
strings
.last_mut()
.unwrap()
.push_str(&serialized_attr.as_str());
}
}
}
Expand All @@ -108,15 +129,18 @@ fn serialize_jsx_element_to_string_vec(
for child in el.children.iter() {
match child {
JSXElementChild::JSXText(jsx_text) => {
strings.last_mut().unwrap().push_str(jsx_text.value.to_string().as_str());
strings
.last_mut()
.unwrap()
.push_str(jsx_text.value.to_string().as_str());
}
JSXElementChild::JSXExprContainer(jsx_expr_container) => {
match &jsx_expr_container.expr {
JSXExpr::JSXEmptyExpr(_jsx_empty_expr) => todo!(),
JSXExpr::Expr(expr) => match &**expr {
Expr::Ident(ident) => {
strings.push("".to_string());
dynamic_exprs.push(ident.sym.to_string());
dynamic_exprs.push(Box::new(Expr::Ident(ident.clone())));
}
_ => todo!(),
},
Expand All @@ -143,26 +167,38 @@ impl JsxString {
let name = create_tpl_binding_name(template_index);
let span = el.span();

self
.templates
.push((template_index, serialize_jsx_element_to_string_vec(el)));
let (static_strs, dynamic_exprs) = serialize_jsx_element_to_string_vec(el);

self.templates.push((template_index, static_strs));

let mut args: Vec<ExprOrSpread> =
Vec::with_capacity(1 + std::cmp::max(1, dynamic_exprs.len()));
args.push(ExprOrSpread {
spread: None,
expr: Box::new(Expr::Ident(Ident::new(name.into(), DUMMY_SP))),
});
if dynamic_exprs.is_empty() {
args.push(ExprOrSpread {
spread: None,
expr: Box::new(Expr::Lit(Lit::Null(Null { span: DUMMY_SP }))),
});
} else {
for dynamic_expr in dynamic_exprs.into_iter() {
args.push(ExprOrSpread {
spread: None,
expr: dynamic_expr,
});
}
}

// $$_tpl_1.join("");
// renderFunction($$_tpl_1, null);
Expr::Call(CallExpr {
span,
callee: Callee::Expr(Box::new(Expr::Member(MemberExpr {
span: DUMMY_SP,
obj: Box::new(Expr::Ident(Ident::new(name.into(), DUMMY_SP))),
prop: MemberProp::Ident(Ident::new("join".into(), DUMMY_SP)),
}))),
args: vec![ExprOrSpread {
spread: None,
expr: Box::new(Expr::Lit(Lit::Str(Str {
span: DUMMY_SP,
value: "".into(),
raw: None,
}))),
}],
callee: Callee::Expr(Box::new(Expr::Ident(Ident::new(
"renderFunction".into(),
DUMMY_SP,
)))),
args,
type_args: Default::default(),
})
}
Expand All @@ -174,7 +210,7 @@ impl VisitMut for JsxString {
fn visit_mut_module(&mut self, module: &mut Module) {
eprintln!("ast {:#?}", module);
module.visit_mut_children_with(self);
for (idx, (strings, dynamic_exprs)) in self.templates.iter().rev() {
for (idx, strings) in self.templates.iter().rev() {
let elems: Vec<Option<ExprOrSpread>> = strings
.iter()
.map(|el| {
Expand Down Expand Up @@ -266,12 +302,23 @@ mod tests {
const b = <div>Hello {name}!</div>;
const c = <button class="btn" onClick={onClick}>Hello {name}!</button>;
"#,
r#"const $$_tpl_1 = ["<div>Hello!</div>"];
const $$_tpl_2 = ["<div>Hello", "!</div>"];
const $$_tpl_3 = ["<button class=\"btn\"", ">Hello ", "!</button>];
const a = $$_tpl_1.join("");
r#"const $$_tpl_1 = [
"<div>Hello!</div>"
];
const $$_tpl_2 = [
"<div>Hello ",
"!</div>"
];
const $$_tpl_3 = [
'<button class="btn"',
">Hello ",
"!</button>"
];
const a = renderFunction($$_tpl_1, null);
const b = renderFunction($$_tpl_2, name);
const c = renderFunction($$_tpl_3, { onClick }, name);"#,
const c = renderFunction($$_tpl_3, {
"onClick": onClick
}, name);"#,
);
}

Expand Down

0 comments on commit d3b72c3

Please sign in to comment.