1use std::{
12 path::Path,
13 sync::{
14 Arc,
15 Mutex,
16 atomic::{AtomicUsize, Ordering},
17 },
18 time::{Duration, Instant},
19};
20
21use tracing::{debug, info, trace, warn};
22
23use super::{
24 Codegen::{self, CodegenConfig},
25 Parser::{self, ParserConfig},
26 Transformer::{self, TransformerConfig},
27};
28
29static COMPILE_ID:AtomicUsize = AtomicUsize::new(0);
30
31#[derive(Debug, Default)]
33pub struct CompilerMetrics {
34 pub count:usize,
36
37 pub elapsed:Duration,
39
40 pub error:usize,
42}
43
44pub struct Compiler {
46 pub config:crate::Struct::SWC::CompilerConfig,
48
49 pub outlook:Arc<Mutex<CompilerMetrics>>,
51}
52
53impl Compiler {
54 pub fn new(config:crate::Struct::SWC::CompilerConfig) -> Self {
56 Self { config, outlook:Arc::new(Mutex::new(CompilerMetrics::default())) }
57 }
58
59 #[tracing::instrument(skip(self, input))]
68 pub fn compile_file(&self, file_path:&str, input:String) -> anyhow::Result<String> {
69 let compile_id = COMPILE_ID.fetch_add(1, Ordering::SeqCst);
70
71 let begin = Instant::now();
72
73 info!("[Compile #{compile_id}] Starting compilation of: {}", file_path);
74
75 trace!("[Compile #{compile_id}] Input size: {} bytes", input.len());
76
77 let (codegen_result, output_path) = {
79 info!("[Compile #{compile_id}] Step 1: Parsing TypeScript source");
80
81 let parser_config = self.get_parser_config();
82
83 let mut parse_result = Parser::parse(&input, file_path, &parser_config)
84 .map_err(|errors| anyhow::anyhow!("Parse errors: {:?}", errors))?;
85
86 info!("[Compile #{compile_id}] Step 1 complete: Parsed {} successfully", file_path);
87
88 debug!(
89 "[Compile #{compile_id}] ParseResult.allocator address: {:p}",
90 &parse_result.allocator
91 );
92
93 debug!(
94 "[Compile #{compile_id}] ParseResult.program address: {:p}",
95 &parse_result.program
96 );
97
98 trace!(
99 "[Compile #{compile_id}] AST body pointer: {:p}",
100 parse_result.program.body.as_ptr()
101 );
102
103 info!("[Compile #{compile_id}] Step 2: Transforming AST");
105
106 let transformer_config = self.get_transformer_config();
107
108 let program = unsafe {
115 std::mem::transmute::<&mut oxc_ast::ast::Program<'static>, &mut oxc_ast::ast::Program<'_>>(
116 &mut parse_result.program,
117 )
118 };
119
120 let source_type = oxc_span::SourceType::from_path(file_path).unwrap_or(oxc_span::SourceType::ts());
121
122 debug!(
123 "[Compile #{compile_id}] Transformer config: target={}, module={}",
124 transformer_config.target, transformer_config.module_format
125 );
126
127 Transformer::transform(&parse_result.allocator, program, file_path, source_type, &transformer_config)
128 .map_err(|errors| anyhow::anyhow!("Transform errors: {:?}", errors))?;
129
130 info!(
131 "[Compile #{compile_id}] Step 2 complete: Transformed {} successfully",
132 file_path
133 );
134
135 trace!(
136 "[Compile #{compile_id}] Program after transform - body pointer: {:p}",
137 program.body.as_ptr()
138 );
139
140 info!("[Compile #{compile_id}] Step 3: Generating code");
142
143 let codegen_config = self.get_codegen_config();
144
145 let codegen_result = Codegen::codegen(&parse_result.allocator, program, source_type, &codegen_config)
146 .map_err(|e| anyhow::anyhow!("Codegen error: {}", e))?;
147
148 info!(
149 "[Compile #{compile_id}] Step 3 complete: Generated {} bytes",
150 codegen_result.code.len()
151 );
152
153 let output_path = Path::new(file_path).with_extension("js");
154
155 (codegen_result, output_path)
156 }; if let Some(parent) = output_path.parent() {
160 std::fs::create_dir_all(parent)?;
161 }
162
163 let write_start = Instant::now();
164
165 std::fs::write(&output_path, &codegen_result.code)?;
166
167 trace!("[Compile #{compile_id}] File write completed in {:?}", write_start.elapsed());
168
169 let elapsed = begin.elapsed();
170
171 {
172 let mut outlook = self.outlook.lock().unwrap();
173
174 outlook.count += 1;
175
176 outlook.elapsed += elapsed;
177 }
178
179 info!("[Compile #{compile_id}] COMPLETE: Compiled {} in {:?}", file_path, elapsed);
180
181 Ok(output_path.to_string_lossy().to_string())
182 }
183
184 #[tracing::instrument(skip(self, input))]
195 pub fn compile_file_to(
196 &self,
197
198 file_path:&str,
199
200 input:String,
201
202 output_path:&Path,
203
204 use_define_for_class_fields:bool,
205 ) -> anyhow::Result<String> {
206 let compile_id = COMPILE_ID.fetch_add(1, Ordering::SeqCst);
207
208 let begin = Instant::now();
209
210 info!(
211 "[Compile #{compile_id}] START compile_file_to: {} -> {}",
212 file_path,
213 output_path.display()
214 );
215
216 trace!(
217 "[Compile #{compile_id}] Input size: {} bytes, use_define_for_class_fields={}",
218 input.len(),
219 use_define_for_class_fields
220 );
221
222 let codegen_result = {
227 info!("[Compile #{compile_id}] Step 1/4: Parsing {}", file_path);
229
230 let parse_start = Instant::now();
231
232 let parser_config = self.get_parser_config();
233
234 let mut parse_result = Parser::parse(&input, file_path, &parser_config)
235 .map_err(|errors| anyhow::anyhow!("Parse errors: {:?}", errors))?;
236
237 info!(
238 "[Compile #{compile_id}] Step 1/4 complete: Parse in {:?}",
239 parse_start.elapsed()
240 );
241
242 debug!(
243 "[Compile #{compile_id}] Memory addresses: allocator={:p}, program={:p}",
244 &parse_result.allocator, &parse_result.program
245 );
246
247 trace!(
248 "[Compile #{compile_id}] AST body slice: ptr={:p}, len={}",
249 parse_result.program.body.as_ptr(),
250 parse_result.program.body.len()
251 );
252
253 info!("[Compile #{compile_id}] Step 2/4: Transforming");
255
256 let transform_start = Instant::now();
257
258 let mut transformer_config = self.get_transformer_config();
259
260 transformer_config.use_define_for_class_fields = use_define_for_class_fields;
261
262 let program = unsafe {
269 std::mem::transmute::<&mut oxc_ast::ast::Program<'static>, &mut oxc_ast::ast::Program<'_>>(
270 &mut parse_result.program,
271 )
272 };
273
274 let source_type = oxc_span::SourceType::from_path(file_path).unwrap_or(oxc_span::SourceType::ts());
275
276 Transformer::transform(&parse_result.allocator, program, file_path, source_type, &transformer_config)
277 .map_err(|errors| anyhow::anyhow!("Transform errors: {:?}", errors))?;
278
279 info!(
280 "[Compile #{compile_id}] Step 2/4 complete: Transform in {:?}",
281 transform_start.elapsed()
282 );
283
284 trace!(
285 "[Compile #{compile_id}] Program after transform - body ptr: {:p}",
286 program.body.as_ptr()
287 );
288
289 info!("[Compile #{compile_id}] Step 3/4: Codegen");
291
292 let codegen_start = Instant::now();
293
294 let codegen_config = self.get_codegen_config();
295
296 let codegen_result = Codegen::codegen(&parse_result.allocator, program, source_type, &codegen_config)
297 .map_err(|e| anyhow::anyhow!("Codegen error: {}", e))?;
298
299 info!(
300 "[Compile #{compile_id}] Step 3/4 complete: Codegen in {:?}, output={}(bytes)",
301 codegen_start.elapsed(),
302 codegen_result.code.len()
303 );
304
305 let _ = program;
308
309 codegen_result
311 }; if let Some(parent) = output_path.parent() {
315 std::fs::create_dir_all(parent)?;
316 }
317
318 let write_start = Instant::now();
320
321 std::fs::write(output_path, &codegen_result.code)?;
322
323 trace!("[Compile #{compile_id}] Step 4/4: File write in {:?}", write_start.elapsed());
324
325 let elapsed = begin.elapsed();
326
327 {
328 let mut outlook = self.outlook.lock().unwrap();
329
330 outlook.count += 1;
331
332 outlook.elapsed += elapsed;
333 }
334
335 info!(
336 "[Compile #{compile_id}] COMPLETE: {} -> {} in {:?}",
337 file_path,
338 output_path.display(),
339 elapsed
340 );
341
342 Ok(output_path.to_string_lossy().to_string())
343 }
344
345 fn get_parser_config(&self) -> ParserConfig {
347 ParserConfig::new(
348 self.config.Target.clone(),
349 self.config.jsx(),
350 true, true, )
353 }
354
355 fn get_transformer_config(&self) -> TransformerConfig {
357 TransformerConfig::new(
358 self.config.Target.clone(),
359 self.config.module_format(),
360 self.config.EmitDecoratorsMetadata,
361 false, self.config.jsx(),
363 self.config.TreeShaking,
364 self.config.Minify,
365 )
366 }
367
368 fn get_codegen_config(&self) -> CodegenConfig {
370 CodegenConfig::new(
371 self.config.Minify,
372 false, String::new(),
374 false, )
376 }
377}