Skip to content

Commit 1beb45a

Browse files
committed
feat: 支持将样式文件中样式与 JSX 节点的 style 属性值合并
1 parent d9eb803 commit 1beb45a

File tree

3 files changed

+135
-21
lines changed

3 files changed

+135
-21
lines changed

asset/mod.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export default class Mod extends React.Component {
3030
render () {
3131
return (
3232
<div className='mod' style={{ width: '500px', height: 800 }}>
33-
<div className='cnt_row'>
33+
<div className='cnt_row' style>
3434
<>
3535
<img
3636
className='icon'

src/utils.rs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::{collections::{HashSet, hash_map::DefaultHasher}, hash::{Hash, Hasher}};
1+
use std::collections::HashSet;
22

33
use html5ever::{QualName, ns, LocalName, namespace_url};
44
use lightningcss::properties::PropertyId;
@@ -95,9 +95,3 @@ pub fn is_style_inheritable(style: PropertyId<'_>) -> bool {
9595
pub fn is_starts_with_uppercase(str: &str) -> bool {
9696
str.chars().next().unwrap().is_uppercase()
9797
}
98-
99-
pub fn calculate_hash<T: Hash>(t: &T) -> u64 {
100-
let mut hasher = DefaultHasher::new();
101-
t.hash(&mut hasher);
102-
hasher.finish()
103-
}

src/visitor.rs

Lines changed: 133 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use ego_tree::{NodeId, Tree, NodeMut, NodeRef};
44
use html5ever::{Attribute, tendril::StrTendril};
55
use lightningcss::{traits::ToCss, stylesheet::PrinterOptions};
66
use swc_common::{Span, DUMMY_SP};
7-
use swc_ecma_ast::{JSXElement, JSXElementName, JSXAttrOrSpread, JSXAttrName, JSXAttrValue, Lit, JSXExpr, Expr, JSXElementChild, Module, Function, Stmt, ExportDefaultExpr, ExportDefaultDecl, DefaultDecl, ClassDecl, ClassMember, PropName, FnDecl, Callee, MemberProp, Str, JSXAttr, Ident};
7+
use swc_ecma_ast::{JSXElement, JSXElementName, JSXAttrOrSpread, JSXAttrName, JSXAttrValue, Lit, JSXExpr, Expr, JSXElementChild, Module, Function, Stmt, ExportDefaultExpr, ExportDefaultDecl, DefaultDecl, ClassDecl, ClassMember, PropName, FnDecl, Callee, MemberProp, Str, JSXAttr, Ident, PropOrSpread, Prop, KeyValueProp};
88
use swc_ecma_visit::{Visit, VisitWith, VisitMut, noop_visit_type, noop_visit_mut_type, VisitMutWith};
99

1010
use crate::{scraper::{Node, Element, Fragment}, utils::{recursion_jsx_member, create_qualname, is_starts_with_uppercase}, style_parser::StyleDeclaration};
@@ -479,12 +479,118 @@ impl<'a> VisitMut for AstMutVisitor<'a> {
479479
let style_record = self.style_record.borrow();
480480
let attrs = &mut n.opening.attrs;
481481
let mut has_style = false;
482+
let mut has_empty_style = false;
482483
for attr in attrs {
483484
if let JSXAttrOrSpread::JSXAttr(attr) = attr {
484485
if let JSXAttrName::Ident(ident) = &attr.name {
485486
if ident.sym.to_string() == "style" {
486487
has_style = true;
487-
break;
488+
// 只支持值为字符串、对象形式的 style
489+
match &mut attr.value {
490+
Some(value) => {
491+
match value {
492+
JSXAttrValue::Lit(lit) => {
493+
match lit {
494+
Lit::Str(str) => {
495+
// 将 style 属性的值转换为对象形式
496+
let mut properties = HashMap::new();
497+
let style = str.value.to_string();
498+
let style = style.split(";").map(|s| s.to_owned()).collect::<Vec<String>>();
499+
if let Some(style_declaration) = style_record.get(node_id) {
500+
for declaration in style_declaration.declaration.declarations.iter() {
501+
let property_id = declaration.property_id().to_css_string(PrinterOptions::default()).unwrap();
502+
let property_value = declaration.value_to_css_string(PrinterOptions::default()).unwrap();
503+
properties.insert(property_id, property_value);
504+
}
505+
}
506+
for property in style.iter() {
507+
let property = property.split(":").map(|s| s.to_owned()).collect::<Vec<String>>();
508+
if property.len() == 2 {
509+
properties.insert(property[0].clone(), property[1].clone());
510+
}
511+
}
512+
let mut style = String::new();
513+
for (property_id, property_value) in properties.iter() {
514+
style.push_str(property_id.as_str());
515+
style.push_str(":");
516+
style.push_str(property_value.as_str());
517+
style.push_str(";");
518+
}
519+
attr.value = Some(JSXAttrValue::Lit(Lit::Str(Str {
520+
span: DUMMY_SP,
521+
value: style.into(),
522+
raw: None,
523+
})));
524+
},
525+
_ => {}
526+
}
527+
},
528+
JSXAttrValue::JSXExprContainer(expr_container) => {
529+
match &mut expr_container.expr {
530+
JSXExpr::JSXEmptyExpr(_) => {
531+
has_empty_style = true;
532+
has_style = false;
533+
},
534+
JSXExpr::Expr(expr) => {
535+
match &mut **expr {
536+
Expr::Object(lit) => {
537+
let mut properties = Vec::new();
538+
if let Some(style_declaration) = style_record.get(node_id) {
539+
for declaration in style_declaration.declaration.declarations.iter() {
540+
let mut has_property = false;
541+
for prop in lit.props.iter_mut() {
542+
match prop {
543+
PropOrSpread::Prop(prop) => {
544+
match &**prop {
545+
Prop::KeyValue(key_value_prop) => {
546+
match &key_value_prop.key {
547+
PropName::Ident(ident) => {
548+
let property_id = ident.sym.to_string();
549+
if property_id == declaration.property_id().to_css_string(PrinterOptions::default()).unwrap() {
550+
has_property = true;
551+
break;
552+
}
553+
},
554+
_ => {}
555+
}
556+
},
557+
_ => {}
558+
}
559+
},
560+
PropOrSpread::Spread(_) => {
561+
}
562+
}
563+
}
564+
if !has_property {
565+
properties.push(declaration.clone());
566+
}
567+
}
568+
}
569+
for property in properties.iter() {
570+
lit.props.push(PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
571+
key: PropName::Ident(Ident::new(property.property_id().to_css_string(PrinterOptions::default()).unwrap().into(), DUMMY_SP)),
572+
value: property.value_to_css_string(PrinterOptions::default()).unwrap().into()
573+
}))));
574+
}
575+
},
576+
_ => {}
577+
}
578+
},
579+
}
580+
},
581+
JSXAttrValue::JSXElement(_) => {
582+
583+
},
584+
JSXAttrValue::JSXFragment(_) => {
585+
586+
}
587+
}
588+
},
589+
None => {
590+
has_empty_style = true;
591+
has_style = false;
592+
}
593+
};
488594
}
489595
}
490596
}
@@ -506,19 +612,33 @@ impl<'a> VisitMut for AstMutVisitor<'a> {
506612
style.push_str(property_value.as_str());
507613
style.push_str(";");
508614
}
509-
n.opening.attrs.push(JSXAttrOrSpread::JSXAttr(JSXAttr {
510-
span: DUMMY_SP,
511-
name: JSXAttrName::Ident(Ident::new("style".into(), DUMMY_SP)),
512-
value: Some(JSXAttrValue::Lit(Lit::Str(Str {
615+
println!("has_empty_style{}", has_empty_style);
616+
if has_empty_style {
617+
for attr in &mut n.opening.attrs {
618+
if let JSXAttrOrSpread::JSXAttr(attr) = attr {
619+
if let JSXAttrName::Ident(ident) = &attr.name {
620+
if ident.sym.to_string() == "style" {
621+
attr.value = Some(JSXAttrValue::Lit(Lit::Str(Str {
622+
span: DUMMY_SP,
623+
value: style.clone().into(),
624+
raw: None,
625+
})));
626+
}
627+
}
628+
}
629+
}
630+
} else {
631+
n.opening.attrs.push(JSXAttrOrSpread::JSXAttr(JSXAttr {
513632
span: DUMMY_SP,
514-
value: style.into(),
515-
raw: None,
516-
})))
517-
}));
633+
name: JSXAttrName::Ident(Ident::new("style".into(), DUMMY_SP)),
634+
value: Some(JSXAttrValue::Lit(Lit::Str(Str {
635+
span: DUMMY_SP,
636+
value: style.into(),
637+
raw: None,
638+
})))
639+
}));
640+
}
518641
}
519-
} else {
520-
// 处理 style 属性为对象的情况
521-
522642
}
523643
}
524644
n.visit_mut_children_with(self);

0 commit comments

Comments
 (0)