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 scoping = semantic_ret.semantic.into_scoping();
144 trace!(
145 "[Transform #{transform_id}] Extracted scoping: {} symbols, {} scopes",
146 scoping.symbols_len(),
147 scoping.scopes_len()
148 );
149
150 let mut typescript_options = TypeScriptOptions::default();
154 typescript_options.only_remove_type_imports = true;
155 trace!("[Transform #{transform_id}] TypeScript options configured (only_remove_type_imports=true)");
156
157 let jsx_options = if config.jsx {
159 JsxOptions { runtime:JsxRuntime::Automatic, ..JsxOptions::default() }
160 } else {
161 JsxOptions { runtime:JsxRuntime::Classic, ..JsxOptions::default() }
163 };
164 trace!("[Transform #{transform_id}] JSX options configured");
165
166 let env_options_start = std::time::Instant::now();
168 let env_options = EnvOptions::from_target(&config.target).unwrap_or_default();
169 trace!(
170 "[Transform #{transform_id}] Env options from target '{}' in {:?}",
171 config.target,
172 env_options_start.elapsed()
173 );
174
175 let mut assumptions = CompilerAssumptions::default();
180 assumptions.set_public_class_fields = !config.use_define_for_class_fields;
181 trace!(
182 "[Transform #{transform_id}] Compiler assumptions configured (set_public_class_fields={})",
183 assumptions.set_public_class_fields
184 );
185
186 let transform_options = TransformOptions {
188 typescript:typescript_options,
189 jsx:jsx_options,
190 env:env_options,
191 assumptions,
192 ..TransformOptions::default()
193 };
194 trace!("[Transform #{transform_id}] TransformOptions configured with plugins");
195 trace!("[Transform #{transform_id}] TransformOptions created");
196
197 let transformer_start = std::time::Instant::now();
199 let transformer = Transformer::new(allocator, Path::new(source_path), &transform_options);
200 info!(
201 "[Transform #{transform_id}] Transformer created in {:?}",
202 transformer_start.elapsed()
203 );
204 trace!("[Transform #{transform_id}] Transformer allocator address: {:p}", allocator);
205
206 let build_start = std::time::Instant::now();
207 let transform_ret = transformer.build_with_scoping(scoping, program);
208 info!(
209 "[Transform #{transform_id}] build_with_scoping completed in {:?}",
210 build_start.elapsed()
211 );
212 trace!(
213 "[Transform #{transform_id}] Program body ptr after: {:p}, len: {}",
214 program.body.as_ptr(),
215 program.body.len()
216 );
217
218 if !transform_ret.errors.is_empty() {
219 let errors:Vec<String> = transform_ret.errors.iter().map(|e| e.to_string()).collect();
220 warn!("[Transform #{transform_id}] Transformation errors: {:?}", errors);
221 return Err(errors);
222 }
223
224 info!(
225 "[Transform #{transform_id}] SUCCESS: Transformation completed for {}",
226 source_path
227 );
228 Ok(())
229}