Skip to content

Commit b4c013c

Browse files
committed
refactor: use Evaluator
1 parent a3d6d34 commit b4c013c

File tree

1 file changed

+41
-136
lines changed
  • packages/formatjs/transform/src

1 file changed

+41
-136
lines changed

packages/formatjs/transform/src/lib.rs

Lines changed: 41 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ use swc_core::{
2020
},
2121
ecma::{
2222
ast::{
23-
ArrayLit, AssignExpr, AssignTarget, Bool, CallExpr, Callee, Expr, ExprOrSpread, Id,
23+
ArrayLit, AssignExpr, AssignTarget, Bool, CallExpr, Callee, Expr, ExprOrSpread, Ident,
2424
IdentName, JSXAttr, JSXAttrName, JSXAttrOrSpread, JSXAttrValue, JSXElementName,
2525
JSXExpr, JSXExprContainer, JSXNamespacedName, JSXOpeningElement, KeyValueProp, Lit,
26-
MemberProp, ModuleItem, Number, ObjectLit, Pat, Prop, PropName, PropOrSpread,
27-
SimpleAssignTarget, Str, VarDeclarator,
26+
MemberProp, ModuleItem, Number, ObjectLit, Prop, PropName, PropOrSpread,
27+
SimpleAssignTarget, Str,
2828
},
2929
visit::{noop_visit_mut_type, VisitMut, VisitMutWith},
3030
},
@@ -48,7 +48,7 @@ pub struct FormatJSPluginOptions {
4848
pub additional_component_names: Vec<String>,
4949
}
5050

51-
fn evaluate_expr(evaluator: &mut Evaluator, expr: &Expr) -> Option<String> {
51+
fn evaluate_expr(expr: &Expr, evaluator: &mut Evaluator) -> Option<String> {
5252
let result = match expr {
5353
Expr::Tpl(tpl) => evaluator.eval_tpl(tpl),
5454
_ => evaluator.eval(expr),
@@ -102,25 +102,22 @@ impl MessageDescriptorExtractor for JSXAttrOrSpread {
102102
if let JSXExpr::Expr(expr) = &container.expr {
103103
match &**expr {
104104
Expr::Ident(ident) => {
105-
let resolved = visitor.resolve_identifier(ident).cloned();
105+
let resolved = visitor.resolve_identifier(ident);
106106
if let Some(resolved_expr) = resolved {
107107
match resolved_expr {
108108
Expr::Object(object_lit) => {
109109
Some(MessageDescriptionValue::Obj(object_lit))
110110
}
111-
expr => evaluate_expression_with_visitor(&expr, visitor)
111+
expr => evaluate_expr(&expr, visitor.evaluator)
112112
.map(MessageDescriptionValue::Str),
113113
}
114114
} else {
115115
None
116116
}
117117
}
118118
Expr::Object(obj) => Some(MessageDescriptionValue::Obj(obj.clone())),
119-
expr => {
120-
// Evaluate the binary expression
121-
evaluate_expression_with_visitor(expr, visitor)
122-
.map(MessageDescriptionValue::Str)
123-
}
119+
expr => evaluate_expr(expr, visitor.evaluator)
120+
.map(MessageDescriptionValue::Str),
124121
}
125122
} else {
126123
None
@@ -153,7 +150,7 @@ impl MessageDescriptorExtractor for PropOrSpread {
153150
if let Prop::KeyValue(key_value) = &**prop {
154151
let key = match &key_value.key {
155152
PropName::Computed(prop_name) => {
156-
evaluate_expression_with_visitor(&prop_name.expr, visitor)
153+
evaluate_expr(&prop_name.expr, visitor.evaluator)
157154
}
158155
PropName::Ident(ident) => Some(ident.sym.to_string()),
159156
PropName::Str(s) => {
@@ -166,8 +163,9 @@ impl MessageDescriptorExtractor for PropOrSpread {
166163
};
167164
let value = match &*key_value.value {
168165
Expr::Object(obj) => Some(MessageDescriptionValue::Obj(obj.clone())),
169-
expr => evaluate_expression_with_visitor(expr, visitor)
170-
.map(MessageDescriptionValue::Str),
166+
expr => {
167+
evaluate_expr(expr, visitor.evaluator).map(MessageDescriptionValue::Str)
168+
}
171169
};
172170
if let (Some(key), Some(value)) = (key, value) {
173171
Some((key, value))
@@ -233,95 +231,6 @@ fn get_message_descriptor_key_from_call_expr(name: &PropName) -> Option<&str> {
233231
// NOTE: Do not support evaluatePath()
234232
}
235233

236-
enum TraversalExpr {
237-
Bin,
238-
Str(String),
239-
}
240-
241-
fn evaluate_expression_with_visitor(
242-
root: &Expr,
243-
visitor: &mut FormatJSVisitor<impl Clone + Comments, impl SourceMapper>,
244-
) -> Option<String> {
245-
match root {
246-
Expr::Bin(_) => {}
247-
_ => {
248-
return evaluate_expr(visitor.evaluator, root);
249-
}
250-
}
251-
252-
// Step 1: Create a post-order representation of the tree using a traversal
253-
// stack. This process simulates recursion iteratively. The resulting
254-
// `post_order_nodes` vector, when read in reverse, gives the correct order
255-
// for evaluation.
256-
let mut post_order_nodes: Vec<TraversalExpr> = Vec::new();
257-
let mut traversal_stack: Vec<&Expr> = vec![root];
258-
259-
while let Some(node) = traversal_stack.pop() {
260-
match node {
261-
Expr::Bin(bin) => {
262-
// Validate the operation. Only '+' is allowed.
263-
if bin.op != swc_core::ecma::ast::BinaryOp::Add {
264-
return None;
265-
}
266-
// Traverse its left and right children.
267-
post_order_nodes.push(TraversalExpr::Bin);
268-
traversal_stack.push(&bin.left);
269-
traversal_stack.push(&bin.right);
270-
}
271-
Expr::Tpl(_) | Expr::Ident(_) | Expr::Lit(_) => {
272-
if let Some(res) = evaluate_expr(visitor.evaluator, node) {
273-
post_order_nodes.push(TraversalExpr::Str(res));
274-
} else {
275-
return None;
276-
}
277-
}
278-
_ => {
279-
// For all other expression types, we cannot evaluate them.
280-
emit_non_evaluable_error(root.span());
281-
}
282-
}
283-
}
284-
285-
// Step 2: Evaluate the expression from the post-order list.
286-
// We use a `result_stack` to hold intermediate string values.
287-
let mut result_stack: Vec<String> = Vec::new();
288-
289-
// We iterate through our collected nodes in reverse to get the correct
290-
// post-order.
291-
for node in post_order_nodes.iter().rev() {
292-
match &&node {
293-
// If it's a literal, just push its value onto the result stack.
294-
TraversalExpr::Str(s) => {
295-
result_stack.push(s.clone());
296-
}
297-
// If it's a binary expression, evaluate it.
298-
TraversalExpr::Bin => {
299-
// A binary operation requires two operands. If we don't have them,
300-
// the tree is malformed (e.g., an operator with only one child).
301-
if result_stack.len() < 2 {
302-
return None;
303-
}
304-
305-
// Pop the operands, concatenate, and push the result.
306-
// Note: The right value was processed last, so it's at the top of the stack.
307-
let right_val = result_stack.pop().unwrap_or_default();
308-
let left_val = result_stack.pop().unwrap_or_default();
309-
310-
result_stack.push(format!("{left_val}{right_val}"));
311-
}
312-
}
313-
}
314-
315-
// Step 3: Final validation and return.
316-
// A correctly formed expression tree will result in exactly one value on the
317-
// stack.
318-
if result_stack.len() == 1 {
319-
Some(result_stack.pop().unwrap())
320-
} else {
321-
None
322-
}
323-
}
324-
325234
#[derive(Debug, Clone, Deserialize)]
326235
pub enum MessageDescriptionValue {
327236
Str(String),
@@ -615,8 +524,6 @@ pub struct FormatJSVisitor<'a, C: Clone + Comments, S: SourceMapper> {
615524
meta: HashMap<String, String>,
616525
component_names: HashSet<String>,
617526
function_names: HashSet<String>,
618-
// Variable tracking for React Compiler optimizations
619-
variable_bindings: HashMap<Id, Expr>,
620527
evaluator: &'a mut Evaluator,
621528
}
622529

@@ -656,7 +563,6 @@ impl<'a, C: Clone + Comments, S: SourceMapper> FormatJSVisitor<'a, C, S> {
656563
meta: Default::default(),
657564
component_names,
658565
function_names,
659-
variable_bindings: Default::default(),
660566
evaluator,
661567
}
662568
}
@@ -688,8 +594,11 @@ impl<'a, C: Clone + Comments, S: SourceMapper> FormatJSVisitor<'a, C, S> {
688594
}
689595
}
690596

691-
fn resolve_identifier(&self, ident: &swc_core::ecma::ast::Ident) -> Option<&Expr> {
692-
self.variable_bindings.get(&ident.to_id())
597+
fn resolve_identifier(&mut self, ident: &Ident) -> Option<Expr> {
598+
self.evaluator
599+
.eval_as_expr(&Expr::Ident(ident.clone()))
600+
.as_deref()
601+
.cloned()
693602
}
694603

695604
fn create_message_descriptor_from_extractor<T: MessageDescriptorExtractor>(
@@ -792,25 +701,19 @@ impl<'a, C: Clone + Comments, S: SourceMapper> FormatJSVisitor<'a, C, S> {
792701
ret
793702
}
794703

795-
fn evaluate_message_descriptor(
796-
&mut self,
797-
descriptor: &mut MessageDescriptor,
798-
// options: &FormatJSPluginOptions,
799-
// filename: &str,
800-
) {
704+
fn evaluate_message_descriptor(&mut self, descriptor: &mut MessageDescriptor) {
801705
let id = &descriptor.id;
802706
let default_message = descriptor.default_message.clone().unwrap_or_default();
803707

804708
let description = descriptor.description.clone();
805709

806710
// Note: do not support override fn
807711
let id = if id.is_none() && !default_message.is_empty() {
808-
let interpolate_pattern =
809-
if let Some(interpolate_pattern) = &self.options.id_interpolation_pattern {
810-
interpolate_pattern.as_str()
811-
} else {
812-
"[sha512:contenthash:base64:6]"
813-
};
712+
let interpolate_pattern = self
713+
.options
714+
.id_interpolation_pattern
715+
.clone()
716+
.unwrap_or("[sha512:contenthash:base64:6]".to_string());
814717

815718
let content = match &description {
816719
Some(MessageDescriptionValue::Str(description)) => {
@@ -882,7 +785,7 @@ impl<'a, C: Clone + Comments, S: SourceMapper> FormatJSVisitor<'a, C, S> {
882785
_ => default_message.clone(),
883786
};
884787

885-
interpolate_name(&self.filename, interpolate_pattern, &content)
788+
interpolate_name(&self.filename, &interpolate_pattern, &content)
886789
} else {
887790
id.clone()
888791
};
@@ -1042,18 +945,21 @@ fn emit_non_evaluable_error(span: Span) {
1042945
impl<'a, C: Clone + Comments, S: SourceMapper> VisitMut for FormatJSVisitor<'a, C, S> {
1043946
noop_visit_mut_type!(fail);
1044947

1045-
fn visit_mut_var_declarator(&mut self, var_declarator: &mut VarDeclarator) {
1046-
var_declarator.visit_mut_children_with(self);
1047-
1048-
// Track variable declarations for React Compiler optimizations
1049-
if let (Pat::Ident(binding_ident), Some(init)) =
1050-
(&var_declarator.name, &var_declarator.init)
1051-
{
1052-
// Store the variable binding
1053-
self.variable_bindings
1054-
.insert(binding_ident.id.to_id(), *init.clone());
1055-
}
1056-
}
948+
// fn visit_mut_var_declarator(&mut self, var_declarator: &mut VarDeclarator) {
949+
// var_declarator.visit_mut_children_with(self);
950+
951+
// // Track variable declarations for React Compiler optimizations
952+
// if let VarDeclarator {
953+
// name: Pat::Ident(binding_ident),
954+
// init: Some(init),
955+
// ..
956+
// } = &var_declarator
957+
// {
958+
// // Store the variable binding
959+
// self.evaluator
960+
// .store(binding_ident.id.to_id(), &init.clone());
961+
// }
962+
// }
1057963

1058964
fn visit_mut_assign_expr(&mut self, assign_expr: &mut AssignExpr) {
1059965
assign_expr.visit_mut_children_with(self);
@@ -1064,7 +970,7 @@ impl<'a, C: Clone + Comments, S: SourceMapper> VisitMut for FormatJSVisitor<'a,
1064970
let variable_id = ident.id.to_id();
1065971

1066972
// Check if we already have a binding for this variable
1067-
let should_update = match self.variable_bindings.get(&variable_id) {
973+
let should_update = match self.resolve_identifier(ident) {
1068974
Some(existing_expr) => {
1069975
// Only overwrite if the new expression is an object literal
1070976
// and the existing one is not, or if both are object literals
@@ -1081,8 +987,7 @@ impl<'a, C: Clone + Comments, S: SourceMapper> VisitMut for FormatJSVisitor<'a,
1081987
};
1082988

1083989
if should_update {
1084-
self.variable_bindings
1085-
.insert(variable_id, *assign_expr.right.clone());
990+
self.evaluator.store(variable_id, &assign_expr.right);
1086991
}
1087992
}
1088993
}

0 commit comments

Comments
 (0)