Skip to content

Commit 86cae60

Browse files
committed
feat: 增加css变量的解析
1 parent 4605431 commit 86cae60

13 files changed

+225
-168
lines changed

__test__/index.spec.mjs.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ Generated by [AVA](https://avajs.dev).
88

99
> Snapshot 1
1010
11-
'{"fonts":[],"keyframes":[],"medias":[],"styles":[{"declarations":[[66,100],[69,"100px"],[67,"100px"],[68,"100px"]],"has_env":false,"media":0,"selector":["px"]}]}'
11+
'{"fonts":[],"keyframes":[],"medias":[],"styles":[{"declarations":[[66,100],[69,"100px"],[67,"100px"],[68,"100px"]],"has_env":false,"media":0,"selector":["px"],"variables":{}}]}'

__test__/index.spec.mjs.snap

12 Bytes
Binary file not shown.

src/constants.rs

+14
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,17 @@ impl SelectorType {
5757
self as u32 as f64
5858
}
5959
}
60+
61+
#[repr(u32)]
62+
#[derive(Hash, PartialEq, Eq, Debug, Clone, Copy)]
63+
pub enum ValueFlag {
64+
None, // 普通类型:0 [22, "100%"]
65+
Variable, // 变量类型:1 [22, "var(--w)", 1]
66+
}
67+
68+
impl ValueFlag {
69+
// 将 SelectorType 枚举值转换为 f64
70+
pub fn to_f64(self) -> f64 {
71+
self as u32 as f64
72+
}
73+
}

src/json_writer.rs

+26-10
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@ use crate::constants::{Pseudo, SUPPORT_PSEUDO_KEYS};
77
use crate::parse_style_properties::DeclsAndVars;
88
use crate::style_propetries::style_value_type::StyleValueType;
99

10-
use crate::style_parser::{FontFaceItem, KeyFrameItem};
10+
use crate::style_parser::{FontFaceItem, KeyFrameItem, RuleItem};
1111
use crate::style_propetries::style_media::StyleMedia;
1212
use crate::style_propetries::unit::Platform;
1313
use crate::visitor::parse_style_values;
1414
use crate::{generate_expr_lit_num, generate_expr_lit_str, utils};
1515

1616
pub struct JsonWriter {
17-
styles: IndexMap<(u32, String), DeclsAndVars>,
17+
styles: Vec<RuleItem>,
1818
keyframes: IndexMap<(u32, String), Vec<KeyFrameItem>>,
1919
medias: Vec<StyleMedia>,
2020
fonts: Vec<FontFaceItem>,
@@ -23,7 +23,7 @@ pub struct JsonWriter {
2323

2424
impl JsonWriter {
2525
pub fn new(
26-
styles: IndexMap<(u32, String), DeclsAndVars>,
26+
styles: Vec<RuleItem>,
2727
keyframes: IndexMap<(u32, String), Vec<KeyFrameItem>>,
2828
medias: Vec<StyleMedia>,
2929
fonts: Vec<FontFaceItem>,
@@ -42,17 +42,17 @@ impl JsonWriter {
4242
let elems: Vec<Expr> = self
4343
.styles
4444
.iter()
45-
.filter_map(|((media_index, selector), item)| {
45+
.filter_map(|rule_item| {
4646
Some({
4747
// 识别伪类
48-
let mut new_selector = selector.clone();
48+
let mut new_selector = rule_item.selector.clone();
4949
let mut pseudo_key = String::new();
5050

5151
if SUPPORT_PSEUDO_KEYS
5252
.into_iter()
53-
.any(|s| selector.contains(s))
53+
.any(|s| rule_item.selector.contains(s))
5454
{
55-
let key_arr = selector.split(":").collect::<Vec<&str>>();
55+
let key_arr = rule_item.selector.split(":").collect::<Vec<&str>>();
5656
if key_arr.len() == 2 {
5757
new_selector = key_arr[0].to_string();
5858
pseudo_key = key_arr[1].to_string();
@@ -63,7 +63,7 @@ impl JsonWriter {
6363
let mut lit_props = vec![
6464
PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
6565
key: PropName::Ident(Ident::new("media".into(), DUMMY_SP)),
66-
value: Box::new(Expr::Lit(Lit::Num(Number::from(*media_index as f64)))),
66+
value: Box::new(Expr::Lit(Lit::Num(Number::from(rule_item.media as f64)))),
6767
}))),
6868
PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
6969
key: PropName::Ident(Ident::new("selector".into(), DUMMY_SP)),
@@ -114,12 +114,28 @@ impl JsonWriter {
114114
key: PropName::Ident(Ident::new("declarations".into(), DUMMY_SP)),
115115
value: Box::new(Expr::Array(ArrayLit {
116116
span: DUMMY_SP,
117-
elems: parse_style_values(item.decls.clone(), Platform::Harmony),
117+
elems: parse_style_values(rule_item.declarations.clone(), Platform::Harmony),
118118
})),
119119
}))),
120+
PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
121+
key: PropName::Ident(Ident::new("variables".into(), DUMMY_SP)),
122+
value: Box::new(Expr::Object(ObjectLit {
123+
span: DUMMY_SP,
124+
props: rule_item.variables.clone().into_iter().map(|css_variable| {
125+
PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
126+
key: PropName::Ident(Ident::new(css_variable.id.clone().into(), DUMMY_SP)),
127+
value: Box::new(Expr::Lit(Lit::Str(Str {
128+
span: DUMMY_SP,
129+
value: css_variable.value.clone().into(),
130+
raw: None,
131+
}))),
132+
})))
133+
}).collect::<Vec<PropOrSpread>>()
134+
}))
135+
}))),
120136
PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
121137
key: PropName::Ident(Ident::new("has_env".into(), DUMMY_SP)),
122-
value: Box::new(Expr::Lit(Lit::Bool(Bool { span: DUMMY_SP, value: item.has_env }))),
138+
value: Box::new(Expr::Lit(Lit::Bool(Bool { span: DUMMY_SP, value: rule_item.has_env }))),
123139
})))
124140
];
125141

src/parse_style_properties.rs

+70-71
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use lightningcss::{
22
properties::{custom::TokenOrValue, Property},
3-
stylesheet::PrinterOptions,
3+
stylesheet::PrinterOptions, traits::ToCss,
44
};
55
use swc_core::ecma::ast::*;
66
use swc_core::{
@@ -15,100 +15,98 @@ use crate::{
1515
generate_expr_lit_str,
1616
style_parser::KeyFrameItem,
1717
style_propetries::{
18-
animation::Animation,
19-
animation_multi::AnimationMulti,
20-
aspect_ratio::AspectRatio,
21-
background::Background,
22-
background_image::BackgroundImage,
23-
background_position::BackgroundPosition,
24-
background_repeat::BackgroundRepeat,
25-
background_size::BackgroundSize,
26-
border::Border,
27-
border_color::BorderColor,
28-
border_radius::BorderRadius,
29-
border_style::BorderStyle,
30-
border_width::BorderWidth,
31-
box_orient::BoxOrient,
32-
box_shadow::BoxShadow,
33-
color::ColorProperty,
34-
display::Display,
35-
expr::Expr,
36-
flex::Flex,
37-
flex_align::FlexAlign,
38-
flex_basis::FlexBasis,
39-
flex_direction::FlexDirection,
40-
flex_wrap::FlexWrap,
41-
font_size::FontSize,
42-
font_style::FontStyle,
43-
font_weight::FontWeight,
44-
gap::Gap,
45-
item_align::ItemAlign,
46-
length_value::LengthValueProperty,
47-
letter_spacing::LetterSpacing,
48-
line_height::LineHeight,
49-
marin_padding::MarginPadding,
50-
max_size::MaxSizeProperty,
51-
normal::Normal,
52-
number::NumberProperty,
53-
opacity::Opacity,
54-
overflow::Overflow,
55-
pointer_events::PointerEvents,
56-
position::Position,
57-
size::SizeProperty,
58-
style_property_type::{string_to_css_property_type, CSSPropertyType},
59-
style_value_type::StyleValueType,
60-
text_align::TextAlign,
61-
text_decoration::TextDecoration,
62-
text_overflow::TextOverflow,
63-
text_shadow::TextShadow,
64-
text_transform::TextTransform,
65-
transform::Transform,
66-
transform_origin::TransformOrigin,
67-
transition::Transition,
68-
unit::{generate_expr_by_length_value, Platform},
69-
vertical_align::VerticalAlign,
70-
visibility::Visibility,
71-
white_space::WhiteSpace,
72-
word_break::WordBreak,
18+
animation::Animation, animation_multi::AnimationMulti, aspect_ratio::AspectRatio, background::Background, background_image::BackgroundImage, background_position::BackgroundPosition, background_repeat::BackgroundRepeat, background_size::BackgroundSize, border::Border, border_color::BorderColor, border_radius::BorderRadius, border_style::BorderStyle, border_width::BorderWidth, box_orient::BoxOrient, box_shadow::BoxShadow, color::ColorProperty, display::Display, expr::Expr, flex::Flex, flex_align::FlexAlign, flex_basis::FlexBasis, flex_direction::FlexDirection, flex_wrap::FlexWrap, font_size::FontSize, font_style::FontStyle, font_weight::FontWeight, gap::Gap, item_align::ItemAlign, length_value::LengthValueProperty, letter_spacing::LetterSpacing, line_height::LineHeight, marin_padding::MarginPadding, max_size::MaxSizeProperty, normal::Normal, number::NumberProperty, opacity::Opacity, overflow::Overflow, pointer_events::PointerEvents, position::Position, size::SizeProperty, style_property_type::{string_to_css_property_type, CSSPropertyType}, style_value_type::{CssVariable, StyleValueType}, text_align::TextAlign, text_decoration::TextDecoration, text_overflow::TextOverflow, text_shadow::TextShadow, text_transform::TextTransform, transform::Transform, transform_origin::TransformOrigin, transition::Transition, unit::{generate_expr_by_length_value, Platform}, variable::Variable, vertical_align::VerticalAlign, visibility::Visibility, white_space::WhiteSpace, word_break::WordBreak
7319

7420
},
7521
utils::lowercase_first,
7622
};
7723

7824
#[derive(Debug, Clone)]
7925
pub struct DeclsAndVars {
80-
pub decls:Vec<StyleValueType>,
26+
pub decls: Vec<StyleValueType>,
27+
pub vars: Vec<CssVariable>,
8128
pub has_env: bool
8229
}
8330

8431
pub fn parse_style_properties(properties: &Vec<(String, Property)>) -> DeclsAndVars {
8532
let mut final_properties = vec![];
33+
let mut variable_properties = vec![];
8634
let mut has_env = false;
87-
for (id, value) in properties.iter() {
35+
for (id, value) in properties.iter() {
36+
let mut is_var: bool = false;
8837
let mut is_env: bool = false;
8938
match value {
9039
Property::Unparsed(unparsed) => {
91-
unparsed.value.0.iter().for_each(|item| match item {
92-
TokenOrValue::Env(env) => {
93-
is_env = true;
94-
let env_result = value.value_to_css_string(PrinterOptions::default());
95-
if (env_result.is_ok()) {
96-
final_properties.push(StyleValueType::Expr(Expr::new(
97-
string_to_css_property_type(id),
98-
generate_expr_lit_str!(env_result.unwrap().to_string()),
99-
)));
100-
}
40+
// 检查是否包含 var() 函数
41+
is_var = unparsed.value.0.iter().any(|token| {
42+
match token {
43+
TokenOrValue::Function(f) => {
44+
// 检查函数内的变量
45+
f.arguments.0.iter().any(|arg| matches!(arg, TokenOrValue::Var(_)))
46+
},
47+
TokenOrValue::Var(_) => true,
48+
_ => false
10149
}
102-
_ => {}
10350
});
51+
52+
// 分析属性值中的所有 token
53+
for token in unparsed.value.0.iter() {
54+
match token {
55+
TokenOrValue::Env(_) => is_env = true,
56+
_ => {}
57+
}
58+
}
59+
60+
// // 处理包含变量的情况
61+
if is_var {
62+
final_properties.push(
63+
StyleValueType::Variable(
64+
Variable::new(
65+
string_to_css_property_type(id),
66+
generate_expr_lit_str!(
67+
value.value_to_css_string(PrinterOptions::default()).unwrap()
68+
)
69+
)
70+
)
71+
);
72+
continue;
73+
}
74+
75+
// 处理环境变量
76+
if is_env {
77+
if let Ok(env_value) = value.value_to_css_string(PrinterOptions::default()) {
78+
has_env = true;
79+
final_properties.push(
80+
StyleValueType::Expr(
81+
Expr::new(
82+
string_to_css_property_type(id),
83+
generate_expr_lit_str!(env_value)
84+
)
85+
)
86+
);
87+
}
88+
continue;
89+
}
90+
},
91+
Property::Custom(custom) => {
92+
let id_ = custom.name.to_css_string(Default::default()).unwrap();
93+
// css 变量
94+
if id_.starts_with("--") {
95+
variable_properties.push(
96+
CssVariable {
97+
id: id_,
98+
value: value.value_to_css_string(PrinterOptions::default()).unwrap().to_string(),
99+
}
100+
);
101+
}
104102
}
105103
_ => {}
106104
};
107-
if is_env {
108-
has_env = true;
105+
if is_env || is_var {
109106
continue;
110107
}
111108

109+
112110
let mut property_name = id.as_str();
113111

114112
// 移除部分厂商前缀: Webkit, Moz, 并且把首字母小写
@@ -549,6 +547,7 @@ pub fn parse_style_properties(properties: &Vec<(String, Property)>) -> DeclsAndV
549547

550548
DeclsAndVars {
551549
has_env: has_env,
550+
vars: variable_properties,
552551
decls: final_properties
553552
}
554553
}

src/style_parser.rs

+26-12
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use std::{cell::RefCell, convert::Infallible, rc::Rc};
22

33
use super::parse_style_properties::parse_style_properties;
44
use crate::parse_style_properties::DeclsAndVars;
5+
use crate::style_propetries::style_value_type::CssVariable;
56
use crate::{generate_expr_enum, generate_expr_lit_str};
67
use crate::style_propetries::font_weight::{self, FontWeight};
78
use crate::style_propetries::style_property_enum::ArkUI_FontWeight;
@@ -29,9 +30,19 @@ use swc_core::ecma::ast::*;
2930
use crate::style_propetries::style_media::StyleMedia;
3031

3132
pub type StyleValue = Vec<StyleValueType>;
33+
34+
#[derive(Debug, Clone)]
35+
pub struct RuleItem {
36+
pub selector: String,
37+
pub media: u32,
38+
pub declarations: Vec<StyleValueType>,
39+
pub variables: Vec<CssVariable>,
40+
pub has_env: bool
41+
}
42+
3243
#[derive(Debug)]
3344
pub struct StyleData {
34-
pub all_style: Rc<RefCell<IndexMap<(u32, String), DeclsAndVars>>>,
45+
pub all_style: Rc<RefCell<Vec<RuleItem>>>,
3546
pub all_keyframes: Rc<RefCell<IndexMap<(u32, String), Vec<KeyFrameItem>>>>,
3647
pub all_medias: Rc<RefCell<Vec<StyleMedia>>>,
3748
pub all_fonts: Rc<RefCell<Vec<FontFaceItem>>>,
@@ -371,17 +382,20 @@ impl<'i> StyleParser<'i> {
371382
let final_all_style = final_all_style
372383
.iter_mut()
373384
.map(|(media_index, selector, properties)| {
374-
(
375-
(media_index.to_owned(), selector.to_owned()),
376-
parse_style_properties(
377-
&properties
378-
.iter()
379-
.map(|(k, v)| (k.to_owned(), v.clone()))
380-
.collect::<Vec<_>>(),
381-
),
382-
)
383-
})
384-
.collect::<IndexMap<(_, _), _>>();
385+
let decls_and_vars = parse_style_properties(
386+
&properties
387+
.iter()
388+
.map(|(k, v)| (k.to_owned(), v.clone()))
389+
.collect::<Vec<_>>()
390+
);
391+
RuleItem {
392+
selector: selector.to_owned(),
393+
media: media_index.to_owned(),
394+
declarations: decls_and_vars.decls,
395+
variables: decls_and_vars.vars,
396+
has_env: decls_and_vars.has_env
397+
}
398+
}).collect::<Vec<RuleItem>>();
385399

386400
let final_all_keyframes = self
387401
.all_keyframes

src/style_propetries/background.rs

+1
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ impl ToExpr for Background {
125125
match position.to_expr() {
126126
PropertyTuple::One(_, val) => props.push((CSSPropertyType::BackgroundPosition, val)),
127127
PropertyTuple::Array(val) => props.extend(val),
128+
PropertyTuple::Variable(_, val) => props.push((CSSPropertyType::BackgroundPosition, val)),
128129
}
129130
}
130131
if let Some(repeat) = &self.repeat {

src/style_propetries/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ pub mod box_shadow;
1616
pub mod color;
1717
pub mod display;
1818
pub mod expr;
19+
pub mod variable;
1920
pub mod flex;
2021
pub mod flex_align;
2122
pub mod flex_basis;

0 commit comments

Comments
 (0)