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
37 pub module_format:String,
39
40 pub emit_decorator_metadata:bool,
42
43 pub use_define_for_class_fields:bool,
45
46 pub jsx:bool,
48
49 pub tree_shaking:bool,
51
52 pub minify:bool,
54}
55
56impl Default for TransformerConfig {
57 fn default() -> Self {
58 Self {
59 target:"es2024".to_string(),
60
61 module_format:"commonjs".to_string(),
62
63 emit_decorator_metadata:true,
64
65 use_define_for_class_fields:false,
66
67 jsx:false,
68
69 tree_shaking:false,
70
71 minify:false,
72 }
73 }
74}
75
76impl TransformerConfig {
77 pub fn new(
79 target:String,
80
81 _module_format:String,
82
83 _emit_decorator_metadata:bool,
84
85 use_define_for_class_fields:bool,
86
87 jsx:bool,
88
89 _tree_shaking:bool,
90
91 _minify:bool,
92 ) -> Self {
93 Self {
94 target,
95
96 module_format:_module_format,
97
98 emit_decorator_metadata:_emit_decorator_metadata,
99
100 use_define_for_class_fields,
101
102 jsx,
103
104 tree_shaking:_tree_shaking,
105
106 minify:_minify,
107 }
108 }
109}
110
111static TRANSFORM_COUNT:AtomicUsize = AtomicUsize::new(0);
123
124#[tracing::instrument(skip(allocator, program, config))]
125pub fn transform<'a>(
126 allocator:&'a Allocator,
127
128 program:&mut Program<'a>,
129
130 source_path:&str,
131
132 _source_type:SourceType,
133
134 config:&TransformerConfig,
135) -> Result<(), Vec<String>> {
136 let transform_id = TRANSFORM_COUNT.fetch_add(1, Ordering::SeqCst);
137
138 info!("[Transform #{transform_id}] Starting transformation of: {}", source_path);
139
140 trace!("[Transform #{transform_id}] Allocator address: {:p}", allocator);
141
142 trace!("[Transform #{transform_id}] Program address: {:p}", program);
143
144 trace!(
145 "[Transform #{transform_id}] Program body ptr before: {:p}, len: {}",
146 program.body.as_ptr(),
147 program.body.len()
148 );
149
150 debug!(
151 "[Transform #{transform_id}] Config: target={}, module={}, use_define={}",
152 config.target, config.module_format, config.use_define_for_class_fields
153 );
154
155 let semantic_start = std::time::Instant::now();
157
158 let semantic_ret = SemanticBuilder::new().build(program);
159
160 info!(
161 "[Transform #{transform_id}] Semantic build completed in {:?}",
162 semantic_start.elapsed()
163 );
164
165 if !semantic_ret.errors.is_empty() {
166 let errors:Vec<String> = semantic_ret.errors.iter().map(|e| e.to_string()).collect();
167
168 warn!("[Transform #{transform_id}] Semantic errors: {:?}", errors);
169
170 return Err(errors);
171 }
172
173 let scoping = semantic_ret.semantic.into_scoping();
180
181 trace!(
182 "[Transform #{transform_id}] Extracted scoping: {} symbols, {} scopes",
183 scoping.symbols_len(),
184 scoping.scopes_len()
185 );
186
187 let mut typescript_options = TypeScriptOptions::default();
191
192 typescript_options.only_remove_type_imports = true;
193
194 trace!("[Transform #{transform_id}] TypeScript options configured (only_remove_type_imports=true)");
195
196 let jsx_options = if config.jsx {
198 JsxOptions { runtime:JsxRuntime::Automatic, ..JsxOptions::default() }
199 } else {
200 JsxOptions { runtime:JsxRuntime::Classic, ..JsxOptions::default() }
202 };
203
204 trace!("[Transform #{transform_id}] JSX options configured");
205
206 let env_options_start = std::time::Instant::now();
208
209 let env_options = EnvOptions::from_target(&config.target).unwrap_or_default();
210
211 trace!(
212 "[Transform #{transform_id}] Env options from target '{}' in {:?}",
213 config.target,
214 env_options_start.elapsed()
215 );
216
217 let mut assumptions = CompilerAssumptions::default();
222
223 assumptions.set_public_class_fields = !config.use_define_for_class_fields;
224
225 trace!(
226 "[Transform #{transform_id}] Compiler assumptions configured (set_public_class_fields={})",
227 assumptions.set_public_class_fields
228 );
229
230 let transform_options = TransformOptions {
232 typescript:typescript_options,
233
234 jsx:jsx_options,
235
236 env:env_options,
237
238 assumptions,
239 ..TransformOptions::default()
240 };
241
242 trace!("[Transform #{transform_id}] TransformOptions configured with plugins");
243
244 trace!("[Transform #{transform_id}] TransformOptions created");
245
246 let transformer_start = std::time::Instant::now();
248
249 let transformer = Transformer::new(allocator, Path::new(source_path), &transform_options);
250
251 info!(
252 "[Transform #{transform_id}] Transformer created in {:?}",
253 transformer_start.elapsed()
254 );
255
256 trace!("[Transform #{transform_id}] Transformer allocator address: {:p}", allocator);
257
258 let build_start = std::time::Instant::now();
259
260 let transform_ret = transformer.build_with_scoping(scoping, program);
261
262 info!(
263 "[Transform #{transform_id}] build_with_scoping completed in {:?}",
264 build_start.elapsed()
265 );
266
267 trace!(
268 "[Transform #{transform_id}] Program body ptr after: {:p}, len: {}",
269 program.body.as_ptr(),
270 program.body.len()
271 );
272
273 if !transform_ret.errors.is_empty() {
274 let errors:Vec<String> = transform_ret.errors.iter().map(|e| e.to_string()).collect();
275
276 warn!("[Transform #{transform_id}] Transformation errors: {:?}", errors);
277
278 return Err(errors);
279 }
280
281 info!(
282 "[Transform #{transform_id}] SUCCESS: Transformation completed for {}",
283 source_path
284 );
285
286 Ok(())
287}