1use std::sync::atomic::{AtomicUsize, Ordering};
10
11use oxc_allocator::Allocator;
12use oxc_ast::ast::Program;
13use oxc_codegen::{Codegen, CodegenOptions, CodegenReturn};
14use oxc_span::SourceType;
15use tracing::{debug, error, info, trace, warn};
16
17#[derive(Debug, Clone)]
19pub struct CodegenConfig {
20 pub minify:bool,
22 pub source_map:bool,
24 pub source_map_name:String,
26 pub comments:bool,
28}
29
30impl Default for CodegenConfig {
31 fn default() -> Self { Self { minify:false, source_map:false, source_map_name:String::new(), comments:false } }
32}
33
34impl CodegenConfig {
35 pub fn new(minify:bool, _source_map:bool, _source_map_name:String, comments:bool) -> Self {
37 Self { minify, source_map:_source_map, source_map_name:_source_map_name, comments }
38 }
39}
40
41pub struct CodegenResult {
43 pub code:String,
45 pub code_len:usize,
47}
48
49fn transform_static_class_properties(code:&str) -> String {
54 let re = match regex::Regex::new(r"(?m)^\s*static\s+([a-zA-Z_$][\w$]*)\s*=\s*([^;]+);") {
57 Ok(re) => re,
58 Err(e) => {
59 error!("transform_static_class_properties: regex compile error: {}", e);
61 return code.to_string();
62 },
63 };
64 re.replace_all(code, "static { this.$1 = $2; }").into_owned()
65}
66
67static CODEGEN_COUNT:AtomicUsize = AtomicUsize::new(0);
78
79#[tracing::instrument(skip(_allocator, program, config))]
80pub fn codegen<'a>(
81 _allocator:&Allocator,
82 program:&Program<'a>,
83 _source_type:SourceType,
84 config:&CodegenConfig,
85) -> Result<CodegenResult, String> {
86 let codegen_id = CODEGEN_COUNT.fetch_add(1, Ordering::SeqCst);
87
88 info!("[Codegen #{codegen_id}] Starting code generation");
89 trace!("[Codegen #{codegen_id}] Program address: {:p}", program);
90 trace!(
91 "[Codegen #{codegen_id}] Program body ptr: {:p}, len: {}",
92 program.body.as_ptr(),
93 program.body.len()
94 );
95 debug!(
96 "[Codegen #{codegen_id}] Config: minify={}, comments={}",
97 config.minify, config.comments
98 );
99
100 let options = CodegenOptions { minify:config.minify, comments:config.comments, ..Default::default() };
102 trace!("[Codegen #{codegen_id}] CodegenOptions configured");
103
104 let codegen_start = std::time::Instant::now();
106 let CodegenReturn { code, .. } = Codegen::new().with_options(options).build(program);
107 info!(
108 "[Codegen #{codegen_id}] Code generation completed in {:?}",
109 codegen_start.elapsed()
110 );
111
112 let code_len = code.len();
113 debug!("[Codegen #{codegen_id}] Generated {} bytes of code", code_len);
114 trace!(
115 "[Codegen #{codegen_id}] First 100 chars of output: {:?}",
116 code.chars().take(100).collect::<String>()
117 );
118
119 info!("[Codegen #{codegen_id}] SUCCESS: Generated {} bytes", code_len);
120 let transformed_code = transform_static_class_properties(&code);
122 Ok(CodegenResult { code:transformed_code, code_len })
123}
124
125pub fn write_output(output_path:&std::path::Path, result:&CodegenResult) -> Result<(), std::io::Error> {
131 if let Some(parent) = output_path.parent() {
133 std::fs::create_dir_all(parent)?;
134 }
135
136 std::fs::write(output_path, &result.code)?;
138
139 debug!("Written output to {}", output_path.display());
140
141 Ok(())
142}