pub mod apply_renames;
pub mod convert_ast;
pub mod convert_ast_reverse;
pub mod convert_scope;
pub mod diagnostics;
pub mod prefilter;
use std::collections::HashMap;
use apply_renames::build_rename_plan;
use convert_ast::convert_program;
use convert_scope::convert_scope_info;
use diagnostics::compile_result_to_diagnostics;
use prefilter::has_react_like_functions;
use react_compiler::entrypoint::compile_result::LoggerEvent;
use react_compiler::entrypoint::plugin_options::PluginOptions;
pub struct TransformResult {
pub file: Option<react_compiler_ast::File>,
pub diagnostics: Vec<oxc_diagnostics::OxcDiagnostic>,
pub events: Vec<LoggerEvent>,
pub rename_plan: HashMap<u32, String>,
}
pub struct LintResult {
pub diagnostics: Vec<oxc_diagnostics::OxcDiagnostic>,
}
pub fn transform(
program: &oxc_ast::ast::Program,
semantic: &oxc_semantic::Semantic,
source_text: &str,
options: PluginOptions,
) -> TransformResult {
if options.compilation_mode != "all" && !has_react_like_functions(program) {
return TransformResult {
file: None,
diagnostics: vec![],
events: vec![],
rename_plan: HashMap::new(),
};
}
let file = convert_program(program, source_text);
let scope_info = convert_scope_info(semantic, program);
let result =
react_compiler::entrypoint::program::compile_program(file, scope_info.clone(), options);
let diagnostics = compile_result_to_diagnostics(&result);
let (program_ast, events, renames) = match result {
react_compiler::entrypoint::compile_result::CompileResult::Success {
ast,
events,
renames,
..
} => (ast, events, renames),
react_compiler::entrypoint::compile_result::CompileResult::Error {
events, ..
} => (None, events, Vec::new()),
};
let rename_plan = build_rename_plan(&scope_info, &renames);
let compiled_file = program_ast.and_then(|raw_json| {
let value: serde_json::Value = serde_json::from_str(raw_json.get()).ok()?;
serde_json::from_value(value).ok()
});
TransformResult {
file: compiled_file,
diagnostics,
events,
rename_plan,
}
}
pub fn transform_source(
source_text: &str,
source_type: oxc_span::SourceType,
options: PluginOptions,
) -> TransformResult {
let allocator = oxc_allocator::Allocator::default();
let parsed = oxc_parser::Parser::new(&allocator, source_text, source_type).parse();
let semantic = oxc_semantic::SemanticBuilder::new()
.build(&parsed.program)
.semantic;
transform(&parsed.program, &semantic, source_text, options)
}
pub fn lint(
program: &oxc_ast::ast::Program,
semantic: &oxc_semantic::Semantic,
source_text: &str,
options: PluginOptions,
) -> LintResult {
let mut opts = options;
opts.no_emit = true;
let result = transform(program, semantic, source_text, opts);
LintResult {
diagnostics: result.diagnostics,
}
}
pub fn emit(
file: &react_compiler_ast::File,
allocator: &oxc_allocator::Allocator,
source_text: Option<&str>,
rename_plan: &HashMap<u32, String>,
) -> String {
let mut program = if let Some(source) = source_text {
convert_ast_reverse::convert_program_to_oxc_with_source(file, allocator, source)
} else {
convert_ast_reverse::convert_program_to_oxc(file, allocator)
};
if let Some(source) = source_text {
let comment_allocator = oxc_allocator::Allocator::default();
let source_type = oxc_span::SourceType::tsx();
let parsed =
oxc_parser::Parser::new(&comment_allocator, source, source_type).parse();
let mut top_level_starts = std::collections::HashSet::new();
top_level_starts.insert(0u32);
for stmt in &program.body {
use oxc_span::GetSpan;
let start = stmt.span().start;
if start > 0 {
top_level_starts.insert(start);
}
}
let mut comments = oxc_allocator::Vec::with_capacity_in(
parsed.program.comments.len(),
allocator,
);
for comment in &parsed.program.comments {
if top_level_starts.contains(&comment.attached_to) {
comments.push(*comment);
}
}
program.comments = comments;
let source_in_alloc =
oxc_allocator::StringBuilder::from_str_in(source, allocator);
program.source_text = source_in_alloc.into_str();
}
apply_renames::apply_renames(&mut program, rename_plan, allocator);
oxc_codegen::Codegen::new().build(&program).code
}
pub fn lint_source(
source_text: &str,
source_type: oxc_span::SourceType,
options: PluginOptions,
) -> LintResult {
let allocator = oxc_allocator::Allocator::default();
let parsed = oxc_parser::Parser::new(&allocator, source_text, source_type).parse();
let semantic = oxc_semantic::SemanticBuilder::new()
.build(&parsed.program)
.semantic;
lint(&parsed.program, &semantic, source_text, options)
}