Skip to content

Commit d9eb803

Browse files
committed
feat: 支持 JSX 节点没有 style 属性情况下,将节点对应的样式写入到 style 属性中
1 parent c35cbdf commit d9eb803

File tree

4 files changed

+120
-48
lines changed

4 files changed

+120
-48
lines changed

src/document.rs

Lines changed: 8 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,25 @@
1-
use std::{collections::HashMap, rc};
1+
use std::collections::HashMap;
22

33
use ego_tree::Tree;
4-
use swc_common::{sync::Lrc, SourceMap, errors::{Handler, ColorConfig}, comments::SingleThreadedComments};
4+
use swc_common::{sync::Lrc, SourceMap, errors::{Handler, ColorConfig}};
55
use swc_ecma_ast::{EsVersion, Module};
6-
use swc_ecma_codegen::{text_writer::JsWriter, Emitter};
76
use swc_ecma_parser::{lexer::Lexer, Syntax, TsConfig, StringInput, Parser};
8-
use swc_ecma_visit::{VisitWith, VisitMutWith};
7+
use swc_ecma_visit::VisitWith;
98

10-
use crate::{scraper::{Node, Selector, ElementRef}, visitor::{AstVisitor, JSXRecord, AstMutVisitor}};
9+
use crate::{scraper::{Node, Selector, ElementRef}, visitor::{AstVisitor, JSXRecord}};
1110

1211
pub struct JSXDocument {
1312
pub tree: Tree<Node>,
1413
pub module: Option<Module>,
14+
pub jsx_record: Option<JSXRecord>,
1515
}
1616

1717
impl JSXDocument {
1818
pub fn new() -> Self {
1919
JSXDocument {
2020
tree: Tree::new(Node::Document),
2121
module: None,
22+
jsx_record: None,
2223
}
2324
}
2425

@@ -57,7 +58,7 @@ impl JSXDocument {
5758
e.into_diagnostic(&handler).emit();
5859
}
5960

60-
let mut module = parser
61+
let module = parser
6162
.parse_module()
6263
.map_err(|e| {
6364
e.into_diagnostic(&handler).emit()
@@ -66,27 +67,8 @@ impl JSXDocument {
6667
let mut jsx_record: JSXRecord = HashMap::new();
6768
let mut vistor = AstVisitor::new(&module, &mut self.tree, &mut jsx_record);
6869
module.visit_with(&mut vistor);
69-
let mut mut_visitor = AstMutVisitor::new( &mut jsx_record);
70-
module.visit_mut_with(&mut mut_visitor);
71-
// // ast 转代码
72-
// let cm = rc::Rc::new(SourceMap::default());
73-
// let handler = Handler::with_tty_emitter(ColorConfig::Auto, true, false, Some(cm.clone()));
74-
// let comments = SingleThreadedComments::default();
75-
76-
// let mut buf = Vec::new();
77-
// {
78-
// let writer = Box::new(JsWriter::new(cm.clone(), "\n", &mut buf, None));
79-
// let mut emitter = Emitter {
80-
// cfg: Default::default(),
81-
// cm: cm.clone(),
82-
// wr: writer,
83-
// comments: Some(&comments),
84-
// };
85-
// emitter.emit_module(&module).unwrap();
86-
// }
87-
// let code = String::from_utf8(buf).unwrap();
88-
// println!("{}", code);
8970
self.module = Some(module);
71+
self.jsx_record = Some(jsx_record);
9072
}
9173

9274
pub fn select<'a>(&self, selector: &'a Selector) -> Vec<ElementRef> {

src/main.rs

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
1-
use std::fs;
1+
use std::{fs, rc::Rc, cell::RefCell};
22

3-
use crate::{document::JSXDocument, style_parser::StyleParser};
3+
use swc_common::{SourceMap, comments::SingleThreadedComments};
4+
use swc_ecma_codegen::{text_writer::JsWriter, Emitter};
5+
6+
use crate::{document::JSXDocument, style_parser::StyleParser, style_write::StyleWrite};
47

58
mod document;
69
mod scraper;
710
mod utils;
811
mod visitor;
912
mod style_parser;
13+
mod style_write;
1014

1115
fn main() {
1216
// 使用 swc 解析 JSX
@@ -17,9 +21,32 @@ fn main() {
1721
document.parse(jsx);
1822

1923
println!();
20-
2124
let mut style_parser = StyleParser::new(&document);
2225
style_parser.parse(&css);
2326
let style_record = style_parser.calc();
24-
println!("{:?}", style_record)
27+
28+
// println!("{:?}", style_record)
29+
let module = Rc::new(RefCell::new(document.module.as_ref().unwrap().clone()));
30+
let jsx_record = Rc::new(RefCell::new(document.jsx_record.as_ref().unwrap().clone()));
31+
let style_record = Rc::new(RefCell::new(style_record));
32+
let mut style_write = StyleWrite::new(module.clone(), jsx_record.clone(), style_record.clone());
33+
style_write.write();
34+
35+
// ast 转代码
36+
let cm = Rc::new(SourceMap::default());
37+
let comments = SingleThreadedComments::default();
38+
39+
let mut buf = Vec::new();
40+
{
41+
let writer = Box::new(JsWriter::new(cm.clone(), "\n", &mut buf, None));
42+
let mut emitter = Emitter {
43+
cfg: Default::default(),
44+
cm: cm.clone(),
45+
wr: writer,
46+
comments: Some(&comments),
47+
};
48+
emitter.emit_module(&module.borrow()).unwrap();
49+
}
50+
let code = String::from_utf8(buf).unwrap();
51+
println!("{}", code);
2552
}

src/style_write.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
use std::{collections::HashMap, rc::Rc, cell::RefCell};
2+
3+
use ego_tree::NodeId;
4+
use swc_ecma_ast::Module;
5+
use swc_ecma_visit::VisitMutWith;
6+
7+
use crate::{visitor::{JSXRecord, AstMutVisitor}, style_parser::StyleDeclaration};
8+
9+
pub struct StyleWrite<'i> {
10+
pub module: Rc<RefCell<Module>>,
11+
pub jsx_record: Rc<RefCell<JSXRecord>>,
12+
pub style_record: Rc<RefCell<HashMap<NodeId, StyleDeclaration<'i>>>>
13+
}
14+
15+
impl<'i> StyleWrite<'i> {
16+
pub fn new(module: Rc<RefCell<Module>>, jsx_record: Rc<RefCell<JSXRecord>>, style_record: Rc<RefCell<HashMap<NodeId, StyleDeclaration<'i>>>>) -> Self {
17+
StyleWrite {
18+
module,
19+
jsx_record,
20+
style_record
21+
}
22+
}
23+
24+
pub fn write(&mut self) {
25+
let mut style_visitor = AstMutVisitor::new(self.jsx_record.clone(), self.style_record.clone());
26+
self.module.borrow_mut().visit_mut_with(&mut style_visitor);
27+
}
28+
}

src/visitor.rs

Lines changed: 53 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
use std::{hash::{Hash, Hasher}, collections::HashMap};
1+
use std::{hash::{Hash, Hasher}, collections::HashMap, rc::Rc, cell::RefCell};
22

33
use ego_tree::{NodeId, Tree, NodeMut, NodeRef};
44
use html5ever::{Attribute, tendril::StrTendril};
5+
use lightningcss::{traits::ToCss, stylesheet::PrinterOptions};
56
use swc_common::{Span, DUMMY_SP};
67
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};
78
use swc_ecma_visit::{Visit, VisitWith, VisitMut, noop_visit_type, noop_visit_mut_type, VisitMutWith};
89

9-
use crate::{scraper::{Node, Element, Fragment}, utils::{recursion_jsx_member, create_qualname, is_starts_with_uppercase, calculate_hash}};
10+
use crate::{scraper::{Node, Element, Fragment}, utils::{recursion_jsx_member, create_qualname, is_starts_with_uppercase}, style_parser::StyleDeclaration};
1011

1112
#[derive(Eq, Clone, Debug)]
1213
pub struct SpanKey(Span);
@@ -458,12 +459,13 @@ impl<'a> Visit for AstVisitor<'a> {
458459
}
459460

460461
pub struct AstMutVisitor<'a> {
461-
pub jsx_record: &'a JSXRecord
462+
pub jsx_record: Rc<RefCell<JSXRecord>>,
463+
pub style_record: Rc<RefCell<HashMap<NodeId, StyleDeclaration<'a>>>>
462464
}
463465

464466
impl<'a> AstMutVisitor<'a> {
465-
pub fn new(jsx_record: &'a JSXRecord) -> Self {
466-
AstMutVisitor { jsx_record }
467+
pub fn new(jsx_record: Rc<RefCell<JSXRecord>>, style_record: Rc<RefCell<HashMap<NodeId, StyleDeclaration<'a>>>>) -> Self {
468+
AstMutVisitor { jsx_record, style_record }
467469
}
468470
}
469471

@@ -472,19 +474,52 @@ impl<'a> VisitMut for AstMutVisitor<'a> {
472474

473475
fn visit_mut_jsx_element(&mut self, n: &mut JSXElement) {
474476
let span_key = SpanKey(n.span);
475-
if let Some(node_id) = self.jsx_record.get(&span_key) {
476-
// 在节点上增加 data-styleid 属性,值为 node_id hash 后的值
477-
let attr_value = JSXAttrValue::Lit(Lit::Str(Str {
478-
span: DUMMY_SP,
479-
value: calculate_hash(node_id).to_string().into(),
480-
raw: None
481-
}));
482-
let attr = JSXAttrOrSpread::JSXAttr(JSXAttr {
483-
span: DUMMY_SP,
484-
name: JSXAttrName::Ident(Ident::new("data-styleid".into(), DUMMY_SP)),
485-
value: Some(attr_value)
486-
});
487-
n.opening.attrs.push(attr);
477+
if let Some(node_id) = self.jsx_record.borrow().get(&span_key) {
478+
// 将 style_record 中的样式添加到 JSXElement 的 style 属性中
479+
let style_record = self.style_record.borrow();
480+
let attrs = &mut n.opening.attrs;
481+
let mut has_style = false;
482+
for attr in attrs {
483+
if let JSXAttrOrSpread::JSXAttr(attr) = attr {
484+
if let JSXAttrName::Ident(ident) = &attr.name {
485+
if ident.sym.to_string() == "style" {
486+
has_style = true;
487+
break;
488+
}
489+
}
490+
}
491+
}
492+
493+
if !has_style {
494+
if let Some(style_declaration) = style_record.get(node_id) {
495+
let mut properties = Vec::new();
496+
for declaration in style_declaration.declaration.declarations.iter() {
497+
properties.push(declaration.clone());
498+
}
499+
500+
let mut style = String::new();
501+
for property in properties.iter() {
502+
let property_id = property.property_id().to_css_string(PrinterOptions::default()).unwrap();
503+
let property_value = property.value_to_css_string(PrinterOptions::default()).unwrap();
504+
style.push_str(property_id.as_str());
505+
style.push_str(":");
506+
style.push_str(property_value.as_str());
507+
style.push_str(";");
508+
}
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 {
513+
span: DUMMY_SP,
514+
value: style.into(),
515+
raw: None,
516+
})))
517+
}));
518+
}
519+
} else {
520+
// 处理 style 属性为对象的情况
521+
522+
}
488523
}
489524
n.visit_mut_children_with(self);
490525
}

0 commit comments

Comments
 (0)