1use std::{
12 path::Path,
13 sync::atomic::{AtomicUsize, Ordering},
14};
15
16use oxc_allocator::Allocator;
17use oxc_ast::ast::Program;
18use oxc_semantic::SemanticBuilder;
19use oxc_span::SourceType;
20use oxc_transformer::{
21 CompilerAssumptions,
22 EnvOptions,
23 JsxOptions,
24 JsxRuntime,
25 TransformOptions,
26 Transformer,
27 TypeScriptOptions,
28};
29use tracing::{debug, info, trace, warn};
30
31#[derive(Debug, Clone)]
33pub struct TransformerConfig {
34 pub target:String,
36 pub module_format:String,
38 pub emit_decorator_metadata:bool,
40 pub use_define_for_class_fields:bool,
42 pub jsx:bool,
44 pub tree_shaking:bool,
46 pub minify:bool,
48}
49
50impl Default for TransformerConfig {
51 fn default() -> Self {
52 Self {
53 target:"es2024".to_string(),
54 module_format:"commonjs".to_string(),
55 emit_decorator_metadata:true,
56 use_define_for_class_fields:false,
57 jsx:false,
58 tree_shaking:false,
59 minify:false,
60 }
61 }
62}
63
64impl TransformerConfig {
65 pub fn new(
67 target:String,
68 _module_format:String,
69 _emit_decorator_metadata:bool,
70 use_define_for_class_fields:bool,
71 jsx:bool,
72 _tree_shaking:bool,
73 _minify:bool,
74 ) -> Self {
75 Self {
76 target,
77 module_format:_module_format,
78 emit_decorator_metadata:_emit_decorator_metadata,
79 use_define_for_class_fields,
80 jsx,
81 tree_shaking:_tree_shaking,
82 minify:_minify,
83 }
84 }
85}
86
87static TRANSFORM_COUNT:AtomicUsize = AtomicUsize::new(0);
99
100#[tracing::instrument(skip(allocator, program, config))]
101pub fn transform<'a>(
102 allocator:&'a Allocator,
103 program:&mut Program<'a>,
104 source_path:&str,
105 _source_type:SourceType,
106 config:&TransformerConfig,
107) -> Result<(), Vec<String>> {
108 let transform_id = TRANSFORM_COUNT.fetch_add(1, Ordering::SeqCst);
109
110 info!("[Transform #{transform_id}] Starting transformation of: {}", source_path);
111 trace!("[Transform #{transform_id}] Allocator address: {:p}", allocator);
112 trace!("[Transform #{transform_id}] Program address: {:p}", program);
113 trace!(
114 "[Transform #{transform_id}] Program body ptr before: {:p}, len: {}",
115 program.body.as_ptr(),
116 program.body.len()
117 );
118 debug!(
119 "[Transform #{transform_id}] Config: target={}, module={}, use_define={}",
120 config.target, config.module_format, config.use_define_for_class_fields
121 );
122
123 let semantic_start = std::time::Instant::now();
125 let semantic_ret = SemanticBuilder::new().build(program);
126 info!(
127 "[Transform #{transform_id}] Semantic build completed in {:?}",
128 semantic_start.elapsed()
129 );
130
131 if !semantic_ret.errors.is_empty() {
132 let errors:Vec<String> = semantic_ret.errors.iter().map(|e| e.to_string()).collect();
133 warn!("[Transform #{transform_id}] Semantic errors: {:?}", errors);
134 return Err(errors);
135 }
136
137 let (symbols, scopes) = semantic_ret.semantic.into_symbol_table_and_scope_tree();
139 trace!(
140 "[Transform #{transform_id}] Extracted {} symbols and {} scopes",
141 symbols.len(),
142 scopes.len()
143 );
144
145 let mut typescript_options = TypeScriptOptions::default();
149 typescript_options.only_remove_type_imports = true;
150 trace!("[Transform #{transform_id}] TypeScript options configured (only_remove_type_imports=true)");
151
152 let jsx_options = if config.jsx {
154 JsxOptions { runtime:JsxRuntime::Automatic, ..JsxOptions::default() }
155 } else {
156 JsxOptions { runtime:JsxRuntime::Classic, ..JsxOptions::default() }
158 };
159 trace!("[Transform #{transform_id}] JSX options configured");
160
161 let env_options_start = std::time::Instant::now();
163 let env_options = EnvOptions::from_target(&config.target).unwrap_or_default();
164 trace!(
165 "[Transform #{transform_id}] Env options from target '{}' in {:?}",
166 config.target,
167 env_options_start.elapsed()
168 );
169
170 let mut assumptions = CompilerAssumptions::default();
175 assumptions.set_public_class_fields = !config.use_define_for_class_fields;
176 trace!(
177 "[Transform #{transform_id}] Compiler assumptions configured (set_public_class_fields={})",
178 assumptions.set_public_class_fields
179 );
180
181 let transform_options = TransformOptions {
183 typescript:typescript_options,
184 jsx:jsx_options,
185 env:env_options,
186 assumptions,
187 ..TransformOptions::default()
188 };
189 trace!("[Transform #{transform_id}] TransformOptions configured with plugins");
190 trace!("[Transform #{transform_id}] TransformOptions created");
191
192 let transformer_start = std::time::Instant::now();
194 let transformer = Transformer::new(allocator, Path::new(source_path), &transform_options);
195 info!(
196 "[Transform #{transform_id}] Transformer created in {:?}",
197 transformer_start.elapsed()
198 );
199 trace!("[Transform #{transform_id}] Transformer allocator address: {:p}", allocator);
200
201 let build_start = std::time::Instant::now();
202 let transform_ret = transformer.build_with_symbols_and_scopes(symbols, scopes, program);
203 info!(
204 "[Transform #{transform_id}] build_with_symbols_and_scopes completed in {:?}",
205 build_start.elapsed()
206 );
207 trace!(
208 "[Transform #{transform_id}] Program body ptr after: {:p}, len: {}",
209 program.body.as_ptr(),
210 program.body.len()
211 );
212
213 if !transform_ret.errors.is_empty() {
214 let errors:Vec<String> = transform_ret.errors.iter().map(|e| e.to_string()).collect();
215 warn!("[Transform #{transform_id}] Transformation errors: {:?}", errors);
216 return Err(errors);
217 }
218
219 info!(
220 "[Transform #{transform_id}] SUCCESS: Transformation completed for {}",
221 source_path
222 );
223 Ok(())
224}