Skip to content

Commit ae6fe8a

Browse files
potetoBoshen
andauthored
[rust-compiler] Return the compiled AST by value instead of JSON (react#36729)
`compile_program` serialized the transformed `File` to a JSON string and every in-process consumer immediately deserialized it back, so each oxc/swc compile paid a full serialize/parse round-trip of the entire program for nothing. `CompileResult::Success.ast` is now `Option<react_compiler_ast::File>`; the napi bridge keeps its JSON boundary by serializing at the edge, while the oxc and swc frontends consume the typed AST directly. This ports the typed-AST patch the Oxc team carries on their fork (co-authored with Boshen), which also makes their integration patch-free going forward. Verified: cargo workspace tests, both snap channels 1804/1804, e2e comparison harness at parity baseline (1802 byte-identical, fbt local-require the one known divergence). First of a stack of three with react#36730 and react#36731. Co-authored-by: Boshen <1430279+Boshen@users.noreply.github.com>
1 parent 34b78a2 commit ae6fe8a

4 files changed

Lines changed: 13 additions & 39 deletions

File tree

compiler/crates/react_compiler/src/entrypoint/compile_result.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use react_compiler_ast::expressions::Identifier as AstIdentifier;
22
use react_compiler_ast::patterns::PatternLike;
33
use react_compiler_ast::statements::BlockStatement;
4+
use react_compiler_ast::File;
45
use react_compiler_diagnostics::SourceLocation;
56
use react_compiler_hir::ReactFunctionType;
67
use serde::Serialize;
@@ -81,10 +82,12 @@ pub struct BindingRenameInfo {
8182
pub enum CompileResult {
8283
/// Compilation succeeded (or no functions needed compilation).
8384
/// `ast` is None if no changes were made to the program.
84-
/// The AST is stored as a pre-serialized JSON string (RawValue) to avoid
85-
/// double-serialization: File→Value→String becomes File→String directly.
85+
/// The compiled Babel AST is returned by value so in-process Rust consumers
86+
/// (the oxc/swc frontends) use it directly instead of round-tripping through
87+
/// JSON. CompileResult still derives Serialize, so the napi consumer
88+
/// serializes the whole result (inlining the File) as before.
8689
Success {
87-
ast: Option<Box<serde_json::value::RawValue>>,
90+
ast: Option<File>,
8891
events: Vec<LoggerEvent>,
8992
/// Unified ordered log interleaving events and debug entries.
9093
/// Items appear in the order they were emitted during compilation.

compiler/crates/react_compiler/src/entrypoint/program.rs

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4020,27 +4020,12 @@ pub fn compile_program(mut file: File, scope: ScopeInfo, options: PluginOptions)
40204020
// Now we can mutate file.program
40214021
apply_compiled_functions(&replacements, &mut file.program, &mut context);
40224022

4023-
// Serialize the modified File AST directly to a JSON string and wrap as RawValue.
4024-
// This avoids double-serialization (File→Value→String) by going File→String directly.
4025-
// The RawValue is embedded verbatim when the CompileResult is serialized.
4026-
let ast = match serde_json::to_string(&file) {
4027-
Ok(s) => match serde_json::value::RawValue::from_string(s) {
4028-
Ok(raw) => Some(raw),
4029-
Err(e) => {
4030-
eprintln!("RUST COMPILER: Failed to create RawValue: {}", e);
4031-
None
4032-
}
4033-
},
4034-
Err(e) => {
4035-
eprintln!("RUST COMPILER: Failed to serialize AST: {}", e);
4036-
None
4037-
}
4038-
};
4039-
40404023
let timing_entries = context.timing.into_entries();
40414024

4025+
// Return the compiled File by value; in-process Rust consumers use it
4026+
// directly, and the napi consumer serializes the whole result as before.
40424027
CompileResult::Success {
4043-
ast,
4028+
ast: Some(file),
40444029
events: context.events,
40454030
ordered_log: context.ordered_log,
40464031
renames: convert_renames(&context.renames),

compiler/crates/react_compiler_oxc/src/lib.rs

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -77,16 +77,8 @@ pub fn transform(
7777
// This maps source positions to new identifier names for uncompiled code.
7878
let rename_plan = build_rename_plan(&scope_info, &renames);
7979

80-
let compiled_file = program_ast.and_then(|raw_json| {
81-
// First parse to serde_json::Value which deduplicates "type" fields
82-
// (the compiler output can produce duplicate "type" keys due to
83-
// BaseNode.node_type + #[serde(tag = "type")] enum tagging)
84-
let value: serde_json::Value = serde_json::from_str(raw_json.get()).ok()?;
85-
serde_json::from_value(value).ok()
86-
});
87-
8880
TransformResult {
89-
file: compiled_file,
81+
file: program_ast,
9082
diagnostics,
9183
events,
9284
rename_plan,

compiler/crates/react_compiler_swc/src/lib.rs

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ pub fn transform(
9191
react_compiler::entrypoint::program::compile_program(file, scope_info, options);
9292

9393
let diagnostics = compile_result_to_diagnostics(&result);
94-
let (program_json, events, renames) = match result {
94+
let (program_ast, events, renames) = match result {
9595
react_compiler::entrypoint::compile_result::CompileResult::Success {
9696
ast,
9797
events,
@@ -103,14 +103,8 @@ pub fn transform(
103103
} => (None, events, Vec::new()),
104104
};
105105

106-
let conversion_result = program_json.and_then(|raw_json| {
107-
// First parse to serde_json::Value which deduplicates "type" fields
108-
// (the compiler output can produce duplicate "type" keys due to
109-
// BaseNode.node_type + #[serde(tag = "type")] enum tagging)
110-
let value: serde_json::Value = serde_json::from_str(raw_json.get()).ok()?;
111-
let file: react_compiler_ast::File = serde_json::from_value(value).ok()?;
112-
let result = convert_program_to_swc_with_source(&file, Some(source_text));
113-
Some(result)
106+
let conversion_result = program_ast.map(|file| {
107+
convert_program_to_swc_with_source(&file, Some(source_text))
114108
});
115109

116110
let (mut swc_module, mut comments) = match conversion_result {

0 commit comments

Comments
 (0)