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 pub elapsed:Duration,
38 pub error:usize,
40}
41
42pub struct Compiler {
44 pub config:crate::Struct::SWC::CompilerConfig,
46 pub outlook:Arc<Mutex<CompilerMetrics>>,
48}
49
50impl Compiler {
51 pub fn new(config:crate::Struct::SWC::CompilerConfig) -> Self {
53 Self { config, outlook:Arc::new(Mutex::new(CompilerMetrics::default())) }
54 }
55
56 #[tracing::instrument(skip(self, input))]
65 pub fn compile_file(&self, file_path:&str, input:String) -> anyhow::Result<String> {
66 let compile_id = COMPILE_ID.fetch_add(1, Ordering::SeqCst);
67 let begin = Instant::now();
68
69 info!("[Compile #{compile_id}] Starting compilation of: {}", file_path);
70 trace!("[Compile #{compile_id}] Input size: {} bytes", input.len());
71
72 let (codegen_result, output_path) = {
74 info!("[Compile #{compile_id}] Step 1: Parsing TypeScript source");
75 let parser_config = self.get_parser_config();
76 let mut parse_result = Parser::parse(&input, file_path, &parser_config)
77 .map_err(|errors| anyhow::anyhow!("Parse errors: {:?}", errors))?;
78
79 info!("[Compile #{compile_id}] Step 1 complete: Parsed {} successfully", file_path);
80 debug!(
81 "[Compile #{compile_id}] ParseResult.allocator address: {:p}",
82 &parse_result.allocator
83 );
84 debug!(
85 "[Compile #{compile_id}] ParseResult.program address: {:p}",
86 &parse_result.program
87 );
88 trace!(
89 "[Compile #{compile_id}] AST body pointer: {:p}",
90 parse_result.program.body.as_ptr()
91 );
92
93 info!("[Compile #{compile_id}] Step 2: Transforming AST");
95 let transformer_config = self.get_transformer_config();
96
97 let program = unsafe {
104 std::mem::transmute::<&mut oxc_ast::ast::Program<'static>, &mut oxc_ast::ast::Program<'_>>(
105 &mut parse_result.program,
106 )
107 };
108 let source_type = oxc_span::SourceType::from_path(file_path).unwrap_or(oxc_span::SourceType::ts());
109
110 debug!(
111 "[Compile #{compile_id}] Transformer config: target={}, module={}",
112 transformer_config.target, transformer_config.module_format
113 );
114
115 Transformer::transform(&parse_result.allocator, program, file_path, source_type, &transformer_config)
116 .map_err(|errors| anyhow::anyhow!("Transform errors: {:?}", errors))?;
117
118 info!(
119 "[Compile #{compile_id}] Step 2 complete: Transformed {} successfully",
120 file_path
121 );
122 trace!(
123 "[Compile #{compile_id}] Program after transform - body pointer: {:p}",
124 program.body.as_ptr()
125 );
126
127 info!("[Compile #{compile_id}] Step 3: Generating code");
129 let codegen_config = self.get_codegen_config();
130 let codegen_result = Codegen::codegen(&parse_result.allocator, program, source_type, &codegen_config)
131 .map_err(|e| anyhow::anyhow!("Codegen error: {}", e))?;
132
133 info!(
134 "[Compile #{compile_id}] Step 3 complete: Generated {} bytes",
135 codegen_result.code.len()
136 );
137
138 let output_path = Path::new(file_path).with_extension("js");
139 (codegen_result, output_path)
140 }; if let Some(parent) = output_path.parent() {
144 std::fs::create_dir_all(parent)?;
145 }
146
147 let write_start = Instant::now();
148 std::fs::write(&output_path, &codegen_result.code)?;
149 trace!("[Compile #{compile_id}] File write completed in {:?}", write_start.elapsed());
150
151 let elapsed = begin.elapsed();
152
153 {
154 let mut outlook = self.outlook.lock().unwrap();
155 outlook.count += 1;
156 outlook.elapsed += elapsed;
157 }
158
159 info!("[Compile #{compile_id}] COMPLETE: Compiled {} in {:?}", file_path, elapsed);
160
161 Ok(output_path.to_string_lossy().to_string())
162 }
163
164 #[tracing::instrument(skip(self, input))]
175 pub fn compile_file_to(
176 &self,
177 file_path:&str,
178 input:String,
179 output_path:&Path,
180 use_define_for_class_fields:bool,
181 ) -> anyhow::Result<String> {
182 let compile_id = COMPILE_ID.fetch_add(1, Ordering::SeqCst);
183 let begin = Instant::now();
184
185 info!(
186 "[Compile #{compile_id}] START compile_file_to: {} -> {}",
187 file_path,
188 output_path.display()
189 );
190 trace!(
191 "[Compile #{compile_id}] Input size: {} bytes, use_define_for_class_fields={}",
192 input.len(),
193 use_define_for_class_fields
194 );
195
196 let codegen_result = {
201 info!("[Compile #{compile_id}] Step 1/4: Parsing {}", file_path);
203 let parse_start = Instant::now();
204 let parser_config = self.get_parser_config();
205 let mut parse_result = Parser::parse(&input, file_path, &parser_config)
206 .map_err(|errors| anyhow::anyhow!("Parse errors: {:?}", errors))?;
207 info!(
208 "[Compile #{compile_id}] Step 1/4 complete: Parse in {:?}",
209 parse_start.elapsed()
210 );
211
212 debug!(
213 "[Compile #{compile_id}] Memory addresses: allocator={:p}, program={:p}",
214 &parse_result.allocator, &parse_result.program
215 );
216 trace!(
217 "[Compile #{compile_id}] AST body slice: ptr={:p}, len={}",
218 parse_result.program.body.as_ptr(),
219 parse_result.program.body.len()
220 );
221
222 info!("[Compile #{compile_id}] Step 2/4: Transforming");
224 let transform_start = Instant::now();
225 let mut transformer_config = self.get_transformer_config();
226 transformer_config.use_define_for_class_fields = use_define_for_class_fields;
227
228 let program = unsafe {
235 std::mem::transmute::<&mut oxc_ast::ast::Program<'static>, &mut oxc_ast::ast::Program<'_>>(
236 &mut parse_result.program,
237 )
238 };
239
240 let source_type = oxc_span::SourceType::from_path(file_path).unwrap_or(oxc_span::SourceType::ts());
241
242 Transformer::transform(&parse_result.allocator, program, file_path, source_type, &transformer_config)
243 .map_err(|errors| anyhow::anyhow!("Transform errors: {:?}", errors))?;
244 info!(
245 "[Compile #{compile_id}] Step 2/4 complete: Transform in {:?}",
246 transform_start.elapsed()
247 );
248 trace!(
249 "[Compile #{compile_id}] Program after transform - body ptr: {:p}",
250 program.body.as_ptr()
251 );
252
253 info!("[Compile #{compile_id}] Step 3/4: Codegen");
255 let codegen_start = Instant::now();
256 let codegen_config = self.get_codegen_config();
257 let codegen_result = Codegen::codegen(&parse_result.allocator, program, source_type, &codegen_config)
258 .map_err(|e| anyhow::anyhow!("Codegen error: {}", e))?;
259 info!(
260 "[Compile #{compile_id}] Step 3/4 complete: Codegen in {:?}, output={}(bytes)",
261 codegen_start.elapsed(),
262 codegen_result.code.len()
263 );
264
265 let _ = program;
268 codegen_result
270 }; if let Some(parent) = output_path.parent() {
274 std::fs::create_dir_all(parent)?;
275 }
276
277 let write_start = Instant::now();
279 std::fs::write(output_path, &codegen_result.code)?;
280 trace!("[Compile #{compile_id}] Step 4/4: File write in {:?}", write_start.elapsed());
281
282 let elapsed = begin.elapsed();
283
284 {
285 let mut outlook = self.outlook.lock().unwrap();
286 outlook.count += 1;
287 outlook.elapsed += elapsed;
288 }
289
290 info!(
291 "[Compile #{compile_id}] COMPLETE: {} -> {} in {:?}",
292 file_path,
293 output_path.display(),
294 elapsed
295 );
296
297 Ok(output_path.to_string_lossy().to_string())
298 }
299
300 fn get_parser_config(&self) -> ParserConfig {
302 ParserConfig::new(
303 self.config.Target.clone(),
304 self.config.jsx(),
305 true, true, )
308 }
309
310 fn get_transformer_config(&self) -> TransformerConfig {
312 TransformerConfig::new(
313 self.config.Target.clone(),
314 self.config.module_format(),
315 self.config.EmitDecoratorsMetadata,
316 false, self.config.jsx(),
318 self.config.TreeShaking,
319 self.config.Minify,
320 )
321 }
322
323 fn get_codegen_config(&self) -> CodegenConfig {
325 CodegenConfig::new(
326 self.config.Minify,
327 false, String::new(),
329 false, )
331 }
332}