Skip to main content

Library/Fn/OXC/
Codegen.rs

1//! OXC Code Generation module
2//!
3//! This module provides code generation from the transformed AST to JavaScript
4//! source code.
5//!
6//! DIAGNOSTIC LOGGING:
7//! - Tracks codegen lifecycle and memory access patterns
8
9use 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/// Codegen configuration options
18#[derive(Debug, Clone)]
19pub struct CodegenConfig {
20	/// Whether to generate minified output
21	pub minify:bool,
22	/// Whether to generate source maps
23	pub source_map:bool,
24	/// Source map file name (without extension)
25	pub source_map_name:String,
26	/// Whether to preserve comments
27	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	/// Create a new codegen configuration
36	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
41/// Result of code generation
42pub struct CodegenResult {
43	/// The generated JavaScript source code
44	pub code:String,
45	/// The length of the generated code
46	pub code_len:usize,
47}
48
49/// Post-process generated JavaScript to match VSCode's static class property
50/// format. Converts `static x = expr;` into `static { this.x = expr; }`.
51/// This is needed because OXC 0.48's class properties plugin does not emit
52/// legacy static initializer blocks by default.
53fn transform_static_class_properties(code:&str) -> String {
54	// This regex matches: static <name> = <expression>;
55	// Captures the property name and the initializer expression.
56	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			// If regex compilation fails, return original code and log.
60			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
67/// Generate JavaScript source code from a transformed AST
68///
69/// # Arguments
70/// * `allocator` - The allocator used for the AST
71/// * `program` - The transformed program AST
72/// * `_source_type` - The source type (JavaScript, JSX, etc.)
73/// * `config` - Codegen configuration options
74///
75/// # Returns
76/// A CodegenResult containing the generated source code
77static 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	// Configure codegen options
101	let options = CodegenOptions { minify:config.minify, comments:config.comments, ..Default::default() };
102	trace!("[Codegen #{codegen_id}] CodegenOptions configured");
103
104	// Create codegen instance and generate code
105	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	// Transform OXC output to match VSCode static class property format
121	let transformed_code = transform_static_class_properties(&code);
122	Ok(CodegenResult { code:transformed_code, code_len })
123}
124
125/// Write the generated code to a file
126///
127/// # Arguments
128/// * `output_path` - The path to write the output file
129/// * `result` - The codegen result containing source text
130pub fn write_output(output_path:&std::path::Path, result:&CodegenResult) -> Result<(), std::io::Error> {
131	// Create parent directories if they don't exist
132	if let Some(parent) = output_path.parent() {
133		std::fs::create_dir_all(parent)?;
134	}
135
136	// Write the source code
137	std::fs::write(output_path, &result.code)?;
138
139	debug!("Written output to {}", output_path.display());
140
141	Ok(())
142}