@@ -118,6 +118,72 @@ impl Default for CompiledOptions {
118118 }
119119}
120120
121+
122+
123+ /// Visitor for collecting variable information only (no CSS processing)
124+ struct VariableInfoCollector < ' a > {
125+ state : & ' a mut TransformState ,
126+ current_var_decl_kind : Option < VarDeclKind > ,
127+ }
128+
129+ impl < ' a > VariableInfoCollector < ' a > {
130+ fn new ( state : & ' a mut TransformState ) -> Self {
131+ Self {
132+ state,
133+ current_var_decl_kind : None ,
134+ }
135+ }
136+ }
137+
138+ impl < ' a > VisitMut for VariableInfoCollector < ' a > {
139+ fn visit_mut_var_decl ( & mut self , n : & mut VarDecl ) {
140+ // Set the current variable declaration kind
141+ self . current_var_decl_kind = Some ( n. kind ) ;
142+
143+ n. visit_mut_children_with ( self ) ;
144+
145+ // Clear the current variable declaration kind
146+ self . current_var_decl_kind = None ;
147+ }
148+
149+ fn visit_mut_var_declarator ( & mut self , n : & mut VarDeclarator ) {
150+ // Track variable declarations
151+ if let Pat :: Ident ( ident) = & n. name {
152+ let var_name = ident. id . sym . to_string ( ) ;
153+
154+ // Track the declaration kind
155+ if let Some ( decl_kind) = self . current_var_decl_kind {
156+ self . state . variable_declaration_kinds . insert ( var_name. clone ( ) , decl_kind) ;
157+ }
158+ }
159+
160+ n. visit_mut_children_with ( self ) ;
161+ }
162+
163+ fn visit_mut_assign_expr ( & mut self , n : & mut AssignExpr ) {
164+ // Track mutations
165+ match & n. left {
166+ AssignTarget :: Simple ( SimpleAssignTarget :: Ident ( ident) ) => {
167+ let var_name = ident. id . sym . to_string ( ) ;
168+ self . state . mutated_variables . insert ( var_name) ;
169+ }
170+ _ => { }
171+ }
172+
173+ n. visit_mut_children_with ( self ) ;
174+ }
175+
176+ fn visit_mut_update_expr ( & mut self , n : & mut UpdateExpr ) {
177+ // Track mutations
178+ if let Expr :: Ident ( ident) = & * n. arg {
179+ let var_name = ident. sym . to_string ( ) ;
180+ self . state . mutated_variables . insert ( var_name) ;
181+ }
182+
183+ n. visit_mut_children_with ( self ) ;
184+ }
185+ }
186+
121187/// Main transformation context for the Compiled SWC plugin.
122188///
123189/// This struct maintains the state throughout the transformation process,
@@ -133,6 +199,8 @@ pub struct CompiledTransform {
133199 pub state : TransformState ,
134200 /// Whether any transformations were applied
135201 pub had_transformations : bool ,
202+ /// Current variable declaration kind (let, const, var) being processed
203+ pub current_var_decl_kind : Option < VarDeclKind > ,
136204}
137205
138206impl CompiledTransform {
@@ -165,6 +233,12 @@ impl CompiledTransform {
165233 // For now, we rely on the string-based approach in the test infrastructure
166234 }
167235
236+ /// Collect variable declarations and mutations without processing CSS
237+ fn collect_variable_info ( & mut self , module : & mut Module ) {
238+ let mut collector = VariableInfoCollector :: new ( & mut self . state ) ;
239+ module. visit_mut_with ( & mut collector) ;
240+ }
241+
168242 /// Process imports and pragmas from the module
169243 fn process_imports_and_pragmas ( & mut self , module : & mut Module ) {
170244 let mut imports_to_remove = Vec :: new ( ) ;
@@ -903,10 +977,16 @@ impl VisitMut for CompiledTransform {
903977 }
904978
905979 fn visit_mut_module ( & mut self , n : & mut Module ) {
906- // First pass: Check for JSX pragmas and compiled imports
980+ // Check for JSX pragmas and compiled imports
907981 self . process_imports_and_pragmas ( n) ;
908982
909- // Second pass: Process child nodes for transformations
983+ // First pass: collect variable declarations and mutations without processing CSS
984+ self . collect_variable_info ( n) ;
985+
986+ // Rebuild the variable context excluding mutated variables
987+ self . state . variable_context = crate :: utils:: variable_context:: build_variable_context_from_module_with_mutations ( n, & self . state . mutated_variables ) ;
988+
989+ // Second pass: process child nodes for transformations with correct variable context
910990 n. visit_mut_children_with ( self ) ;
911991
912992 // Finalize the module
@@ -932,7 +1012,7 @@ impl VisitMut for CompiledTransform {
9321012 // Handle different types of tagged templates
9331013 if visitors:: styled:: visit_styled_tagged_template ( n, & mut self . state , & mut self . collected_css_sheets ) {
9341014 self . had_transformations = true ;
935- } else if visitors:: keyframes:: visit_keyframes_tagged_template ( n, & mut self . state ) {
1015+ } else if visitors:: keyframes:: visit_keyframes_tagged_template ( n, & mut self . state , & mut self . collected_css_sheets ) {
9361016 self . had_transformations = true ;
9371017 }
9381018
@@ -952,11 +1032,30 @@ impl VisitMut for CompiledTransform {
9521032 n. visit_mut_children_with ( self ) ;
9531033 }
9541034
1035+ fn visit_mut_var_decl ( & mut self , n : & mut VarDecl ) {
1036+ // Store the current variable declaration kind
1037+ self . current_var_decl_kind = Some ( n. kind ) ;
1038+
1039+ // Visit children (including declarators)
1040+ n. visit_mut_children_with ( self ) ;
1041+
1042+ // Clear the current variable declaration kind
1043+ self . current_var_decl_kind = None ;
1044+ }
1045+
9551046 fn visit_mut_var_declarator ( & mut self , n : & mut VarDeclarator ) {
9561047 // Track local variables before visiting children
9571048 if let Pat :: Ident ( ident) = & n. name {
9581049 if let Some ( init_expr) = & n. init {
9591050 let var_name = ident. id . sym . to_string ( ) ;
1051+
1052+ // Track the declaration kind
1053+ if let Some ( decl_kind) = self . current_var_decl_kind {
1054+ self . state . variable_declaration_kinds . insert ( var_name. clone ( ) , decl_kind) ;
1055+ }
1056+
1057+ // Track all variables for static analysis initially
1058+ // We'll filter out actually mutated ones during evaluation
9601059 self . track_local_variable ( & var_name, init_expr) ;
9611060 }
9621061 }
@@ -970,6 +1069,34 @@ impl VisitMut for CompiledTransform {
9701069 }
9711070 }
9721071
1072+ /// Track assignments to variables (mutations)
1073+ fn visit_mut_assign_expr ( & mut self , n : & mut AssignExpr ) {
1074+ // Check if this is an assignment to a variable
1075+ match & n. left {
1076+ AssignTarget :: Simple ( SimpleAssignTarget :: Ident ( ident) ) => {
1077+ let var_name = ident. id . sym . to_string ( ) ;
1078+ // Mark this variable as mutated
1079+ self . state . mutated_variables . insert ( var_name) ;
1080+ }
1081+ _ => { }
1082+ }
1083+
1084+ n. visit_mut_children_with ( self ) ;
1085+ }
1086+
1087+ /// Track update expressions (mutations like var++, ++var, var--, --var)
1088+ fn visit_mut_update_expr ( & mut self , n : & mut UpdateExpr ) {
1089+ if let Expr :: Ident ( ident) = & * n. arg {
1090+ let var_name = ident. sym . to_string ( ) ;
1091+ // Mark this variable as mutated
1092+ self . state . mutated_variables . insert ( var_name) ;
1093+ }
1094+
1095+ n. visit_mut_children_with ( self ) ;
1096+ }
1097+
1098+
1099+
9731100
9741101}
9751102
@@ -989,6 +1116,7 @@ pub fn process_transform(program: Program, metadata: TransformPluginProgramMetad
9891116 css_content_to_var : HashMap :: new ( ) ,
9901117 state,
9911118 had_transformations : false ,
1119+ current_var_decl_kind : None ,
9921120 } ;
9931121
9941122 program. fold_with ( & mut as_folder ( & mut transform) )
0 commit comments