Skip to content

Commit e75aa7f

Browse files
committed
feat: 支持解析高阶组件
1 parent 1e88577 commit e75aa7f

File tree

7 files changed

+274
-32
lines changed

7 files changed

+274
-32
lines changed

__test__/fixure/pesudo.jsx

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,5 @@ import './pesudo.scss'
33

44
export default function Pesudo() {
55

6-
return <View className='pesudo' style={{
7-
height: '10px',
8-
width: Math.random() > 0.5 ? '10px' : '20px'
9-
}}>
10-
<Text className='text'>asdasdasdasdasd</Text>
11-
</View>
6+
return <View className='pesudo'></View>
127
}

__test__/fixure/pesudo.scss

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,13 @@
1-
.pesudo {
2-
height: 400px;
3-
width: 400px;
4-
margin-top: calc(100% - 30px);
1+
:root {
2+
--color: #403635 + #403635;
3+
--angle: 30deg;
4+
--length: 200px;
5+
--res-olution: 300dpi;
6+
--url: url("https://www.google.com");
7+
--var: var(--color, '48px');
58
}
69

7-
.pesudo:last-child {
8-
color: red;
9-
}
10-
11-
12-
.aaa {
13-
font-size: 20px;
14-
-webkit-line-clamp: 2;
15-
text-overflow: ellipsis;
16-
color: #00f;
17-
}
10+
.hello {
11+
height: 30px;
12+
color: var(--color);
13+
}

index.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,7 @@ export interface ParseOptions {
77
platformString: string
88
isEnableNesting?: boolean
99
}
10+
export interface ParseResult {
11+
code: string
12+
}
1013
export function parse(component: string, styles: Array<string>, options: ParseOptions): string

src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ pub struct ParseOptions {
3232
pub is_enable_nesting: Option<bool>,
3333
}
3434

35+
#[napi(object)]
36+
pub struct ParseResult {
37+
pub code: String,
38+
}
39+
3540
#[napi]
3641
pub fn parse(component: String, styles: Vec<String>, options: ParseOptions) -> String {
3742

@@ -85,5 +90,6 @@ pub fn parse(component: String, styles: Vec<String>, options: ParseOptions) -> S
8590
emitter.emit_program(&program.borrow()).unwrap();
8691
}
8792
let code = String::from_utf8(buf).unwrap().replace("\r\n", "\n");
93+
8894
code
8995
}

src/parse_css_variables.rs

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
use std::{collections::HashMap, rc::Rc};
2+
3+
use lightningcss::{properties::{custom::TokenOrValue, Property}, traits::ToCss, values::time::Time};
4+
use swc_common::{comments::SingleThreadedComments, sync::Lrc, SourceMap, DUMMY_SP};
5+
use swc_ecma_ast::{AssignExpr, AssignOp, CallExpr, Callee, ComputedPropName, Expr, ExprOrSpread, ExprStmt, KeyValueProp, Lit, MemberExpr, MemberProp, Module, ModuleItem, Number, ObjectLit, PatOrExpr, Program, Prop, PropName, PropOrSpread, Stmt};
6+
use swc_ecma_codegen::{text_writer::JsWriter, Emitter};
7+
use swc_ecma_utils::quote_ident;
8+
9+
use crate::style_propetries::unit::{convert_color_keywords_to_hex, generate_expr_by_length_value, Platform};
10+
11+
12+
pub fn parse(properties: Vec<(String, Property<'_>)>) -> HashMap<String, Expr> {
13+
14+
// properties.iter().for_each(|(key, value)| {
15+
// println!("key: {}, value: {:?}", key, value);
16+
// });
17+
let mut css_variables = HashMap::new();
18+
properties.iter().for_each(|(key, value)| {
19+
let mut expr: Option<Expr> = None;
20+
match value.clone() {
21+
Property::Custom(custom) => {
22+
let token_or_value = custom.value.0.get(0);
23+
if let Some(token_or_value) = token_or_value {
24+
expr = Some(get_token_or_value(token_or_value.to_owned()));
25+
}
26+
},
27+
_ => {}
28+
};
29+
if let Some(expr) = expr {
30+
css_variables.insert(key.to_string(), expr);
31+
}
32+
});
33+
css_variables
34+
}
35+
36+
37+
pub fn write(css_variables: HashMap<String, Expr>) -> String {
38+
// css_variables.iter().for_each(|(key, value)| {
39+
// println!("key: {}, value: {:?}", key, value);
40+
// });
41+
42+
let obj = Expr::Object(ObjectLit {
43+
span: DUMMY_SP,
44+
props: css_variables.iter().map(|(key, value)| {
45+
PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
46+
key: PropName::Computed(ComputedPropName {
47+
span: DUMMY_SP,
48+
expr: Box::new(Expr::Lit(Lit::Str(key.to_string().into()))),
49+
}),
50+
value: Box::new(value.clone()),
51+
})))
52+
}).collect::<Vec<PropOrSpread>>().into(),
53+
});
54+
55+
let cm: Lrc<SourceMap> = Default::default();
56+
let comments = SingleThreadedComments::default();
57+
let program = Program::Module(Module {
58+
span: DUMMY_SP,
59+
body: vec![
60+
ModuleItem::Stmt(
61+
Stmt::Expr(ExprStmt {
62+
span: DUMMY_SP,
63+
expr: Box::new(Expr::Assign(AssignExpr {
64+
span: DUMMY_SP,
65+
op: AssignOp::Assign,
66+
left: PatOrExpr::Expr(Box::new(Expr::Ident(quote_ident!("css_var_map")))),
67+
right: Box::new(obj)
68+
}))
69+
})
70+
),
71+
],
72+
shebang: None,
73+
});
74+
// 生成代码
75+
let mut buf = vec![];
76+
{
77+
let mut emitter = Emitter {
78+
cfg: swc_ecma_codegen::Config::default(),
79+
cm: cm.clone(),
80+
comments: Some(&comments),
81+
wr: JsWriter::new(cm.clone(), "\n", &mut buf, None),
82+
};
83+
emitter.emit_program(&program).unwrap();
84+
}
85+
String::from_utf8(buf).unwrap().replace("\r\n", "\n")
86+
}
87+
88+
pub fn get_token_or_value (token_or_value: TokenOrValue<'_>) -> Expr {
89+
match token_or_value {
90+
TokenOrValue::Token(_) => {
91+
Expr::Lit(Lit::Str("".into()))
92+
},
93+
TokenOrValue::Color(color) => {
94+
let color_string = convert_color_keywords_to_hex(color.to_css_string(lightningcss::stylesheet::PrinterOptions {
95+
minify: false,
96+
targets: lightningcss::targets::Targets {
97+
include: lightningcss::targets::Features::HexAlphaColors,
98+
..lightningcss::targets::Targets::default()
99+
},
100+
..lightningcss::stylesheet::PrinterOptions::default()
101+
}).unwrap());
102+
Expr::Lit(Lit::Str(color_string.into()))
103+
},
104+
TokenOrValue::UnresolvedColor(_) => {
105+
// 解析不到的颜色
106+
Expr::Lit(Lit::Str("".into()))
107+
},
108+
TokenOrValue::Url(url) => {
109+
// url("https://www.example.com") => { src: "https://www.example.com" }
110+
let url_string = url.url.to_string();
111+
Expr::Object(ObjectLit {
112+
span: DUMMY_SP,
113+
props: vec![
114+
PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
115+
key: PropName::Ident(quote_ident!("src")),
116+
value: Box::new(Expr::Lit(Lit::Str(url_string.into()))),
117+
})))
118+
]
119+
.into(),
120+
})
121+
},
122+
TokenOrValue::Var(var) => {
123+
// var(--color-primary, #000000) => var_fn(css_var_map["--color-primary"], "#000000")
124+
let mut expr_or_spead = vec![];
125+
let ident_string = var.name.to_css_string(Default::default()).unwrap();
126+
expr_or_spead.push(ExprOrSpread {
127+
spread: None,
128+
expr: Box::new(
129+
Expr::Member(MemberExpr {
130+
span: DUMMY_SP,
131+
obj: Box::new(Expr::Ident(quote_ident!("css_var_map"))),
132+
prop: MemberProp::Computed(ComputedPropName {
133+
span: DUMMY_SP,
134+
expr: Box::new(Expr::Lit(Lit::Str(ident_string.into()))),
135+
})
136+
})
137+
)
138+
});
139+
if let Some(fallback) = &var.fallback {
140+
fallback.0.iter().for_each(|token_or_value| {
141+
expr_or_spead.push(ExprOrSpread {
142+
spread: None,
143+
expr: Box::new(get_token_or_value(token_or_value.to_owned())),
144+
})
145+
});
146+
}
147+
Expr::Call(CallExpr {
148+
span: Default::default(),
149+
callee: Callee::Expr(Box::new(Expr::Ident(quote_ident!("var_fn")))),
150+
args: expr_or_spead,
151+
type_args: None,
152+
})
153+
154+
},
155+
TokenOrValue::Env(_) => {
156+
// 环境变量
157+
Expr::Lit(Lit::Str("".into()))
158+
},
159+
TokenOrValue::Function(_) => {
160+
// 函数
161+
Expr::Lit(Lit::Str("".into()))
162+
},
163+
TokenOrValue::Length(length_value) => {
164+
generate_expr_by_length_value(&length_value, Platform::Harmony)
165+
},
166+
TokenOrValue::Angle(angle) => {
167+
let angle_string = angle.to_css_string(Default::default()).unwrap();
168+
Expr::Lit(Lit::Str(angle_string.into()))
169+
},
170+
TokenOrValue::Time(time) => {
171+
let time_num = match time {
172+
Time::Seconds(s) => s,
173+
Time::Milliseconds(m) => m * 60.0,
174+
};
175+
Expr::Lit(Lit::Num(Number {
176+
span: DUMMY_SP,
177+
value: time_num as f64,
178+
raw: None,
179+
}))
180+
},
181+
TokenOrValue::Resolution(resolution) => {
182+
let string = resolution.to_css_string(Default::default()).unwrap();
183+
Expr::Lit(Lit::Str(string.into()))
184+
},
185+
TokenOrValue::DashedIdent(dashed_ident) => {
186+
let ident_string = dashed_ident.to_css_string(Default::default()).unwrap();
187+
Expr::Member(MemberExpr {
188+
span: DUMMY_SP,
189+
obj: Box::new(Expr::Ident(quote_ident!("css_var_map"))),
190+
prop: MemberProp::Computed(ComputedPropName {
191+
span: DUMMY_SP,
192+
expr: Box::new(Expr::Lit(Lit::Str(ident_string.into()))),
193+
})
194+
})
195+
},
196+
}
197+
}

src/style_parser.rs

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ pub type StyleValue = Vec<StyleValueType>;
1111
pub struct StyleData<'i> {
1212
pub pesudo_style_record: Rc<RefCell<HashMap<SpanKey, Vec<(String, Vec<(String, Property<'i>)>)>>>>,
1313
pub all_style: Rc<RefCell<HashMap<String, StyleValue>>>,
14+
pub css_variables: Rc<RefCell<Vec<(String, Property<'i>)>>>,
1415
pub has_nesting: bool
1516
}
1617

@@ -96,12 +97,26 @@ impl<'i> StyleParser<'i> {
9697
let mut all_style = self.all_style.borrow_mut();
9798
let mut style_record = HashMap::new();
9899
let mut pesudo_style_record = HashMap::new();
99-
let mut final_all_style = self.calc_style_record(&mut all_style);
100+
let mut css_variables = vec![];
100101
// 是否含有嵌套选择器
101102
let mut has_nesting = false;
102103

103104
// final_all_style 转换为驼峰命名
104-
let mut final_all_style = final_all_style.iter_mut().map(|(selector, style_value)| {
105+
let mut final_all_style = vec![];
106+
self.calc_style_record(&mut all_style).iter_mut().for_each(|(selector, style_value)| {
107+
// 判断是否含有伪类:root
108+
if selector == ":root" {
109+
css_variables = style_value.declaration.declarations.iter().map(|property| {
110+
(
111+
property
112+
.property_id()
113+
.to_css_string(PrinterOptions::default())
114+
.unwrap(),
115+
property.clone(),
116+
)
117+
}).collect::<Vec<(_, _)>>();
118+
return;
119+
}
105120
let properties = style_value.declaration.declarations.iter().map(|property| {
106121
(
107122
to_camel_case(
@@ -120,16 +135,15 @@ impl<'i> StyleParser<'i> {
120135
if selector.contains(" ") || selector.chars().filter(|&c| c == '.').count() > 1 {
121136
has_nesting = true
122137
}
123-
(selector.to_owned(), properties)
124-
})
125-
.collect::<Vec<(_, _)>>();
138+
final_all_style.push((selector.to_owned(), properties));
139+
});
126140

127141
let mut pesudo_selector = None;
128142
for (selector, style_value) in final_all_style.iter_mut() {
129143
// 用于查询的选择器
130144
let mut element_selector = selector.clone();
131145
// 判断是否伪类(暂时支持鸿蒙)
132-
if selector.contains(":") && self.platform == Platform::Harmony {
146+
if (selector.contains(":before") || selector.contains(":after")) && self.platform == Platform::Harmony {
133147
let _selectors = selector.split(":").collect::<Vec<&str>>();
134148
pesudo_selector = _selectors[1].parse::<String>().ok();
135149
// 伪类需要把 : 之后的选择器去掉,只保留 : 之前的选择器,用于查询所属的element
@@ -172,6 +186,7 @@ impl<'i> StyleParser<'i> {
172186
StyleData {
173187
pesudo_style_record: Rc::new(RefCell::new(final_pesudo_style_record)),
174188
all_style: Rc::new(RefCell::new(final_all_style)),
189+
css_variables: Rc::new(RefCell::new(css_variables)),
175190
has_nesting
176191
}
177192
}

0 commit comments

Comments
 (0)