main
rs 296 lines 10.2 KB
Raw
1 use react_compiler_ast::File;
2 use react_compiler_ast::expressions::Identifier as AstIdentifier;
3 use react_compiler_ast::patterns::PatternLike;
4 use react_compiler_ast::statements::BlockStatement;
5 use react_compiler_diagnostics::SourceLocation;
6 use react_compiler_hir::ReactFunctionType;
7 use serde::Serialize;
8
9 use crate::timing::TimingEntry;
10
11 /// Source location with index and filename fields for logger event serialization.
12 /// Matches the Babel SourceLocation format that the TS compiler emits in logger events.
13 #[derive(Debug, Clone, Serialize)]
14 pub struct LoggerSourceLocation {
15 pub start: LoggerPosition,
16 pub end: LoggerPosition,
17 #[serde(skip_serializing_if = "Option::is_none")]
18 pub filename: Option<String>,
19 #[serde(rename = "identifierName", skip_serializing_if = "Option::is_none")]
20 pub identifier_name: Option<String>,
21 }
22
23 #[derive(Debug, Clone, Serialize)]
24 pub struct LoggerPosition {
25 pub line: u32,
26 pub column: u32,
27 #[serde(skip_serializing_if = "Option::is_none")]
28 pub index: Option<u32>,
29 }
30
31 impl LoggerSourceLocation {
32 /// Create from a diagnostics SourceLocation, adding index and filename.
33 pub fn from_loc(
34 loc: &SourceLocation,
35 filename: Option<&str>,
36 start_index: Option<u32>,
37 end_index: Option<u32>,
38 ) -> Self {
39 Self {
40 start: LoggerPosition {
41 line: loc.start.line,
42 column: loc.start.column,
43 index: start_index,
44 },
45 end: LoggerPosition {
46 line: loc.end.line,
47 column: loc.end.column,
48 index: end_index,
49 },
50 filename: filename.map(|s| s.to_string()),
51 identifier_name: None,
52 }
53 }
54
55 /// Create from a diagnostics SourceLocation without index or filename.
56 pub fn from_loc_simple(loc: &SourceLocation) -> Self {
57 Self {
58 start: LoggerPosition {
59 line: loc.start.line,
60 column: loc.start.column,
61 index: None,
62 },
63 end: LoggerPosition {
64 line: loc.end.line,
65 column: loc.end.column,
66 index: None,
67 },
68 filename: None,
69 identifier_name: None,
70 }
71 }
72 }
73
74 /// A variable rename from lowering, serialized for the JS shim.
75 #[derive(Debug, Clone, Serialize)]
76 pub struct BindingRenameInfo {
77 pub original: String,
78 pub renamed: String,
79 #[serde(rename = "declarationStart")]
80 pub declaration_start: u32,
81 }
82
83 /// Main result type returned by the compile function.
84 /// Serialized to JSON and returned to the JS shim.
85 #[derive(Debug, Serialize)]
86 #[serde(tag = "kind", rename_all = "lowercase")]
87 pub enum CompileResult {
88 /// Compilation succeeded (or no functions needed compilation).
89 /// `ast` is None if no changes were made to the program.
90 /// The compiled Babel AST is returned by value so in-process Rust consumers
91 /// (the oxc/swc frontends) use it directly instead of round-tripping through
92 /// JSON. CompileResult still derives Serialize, so the napi consumer
93 /// serializes the whole result (inlining the File) as before.
94 Success {
95 ast: Option<File>,
96 events: Vec<LoggerEvent>,
97 /// Unified ordered log interleaving events and debug entries.
98 /// Items appear in the order they were emitted during compilation.
99 /// The JS side uses this as the single source of truth (preferred over
100 /// separate events/debugLogs arrays).
101 #[serde(rename = "orderedLog", skip_serializing_if = "Vec::is_empty")]
102 ordered_log: Vec<OrderedLogItem>,
103 /// Variable renames from lowering, for applying back to the Babel AST.
104 /// Each entry maps an original binding name to its renamed version,
105 /// identified by the binding's declaration start position in the source.
106 #[serde(skip_serializing_if = "Vec::is_empty")]
107 renames: Vec<BindingRenameInfo>,
108 /// Timing data for profiling. Only populated when __profiling is enabled.
109 #[serde(skip_serializing_if = "Vec::is_empty")]
110 timing: Vec<TimingEntry>,
111 },
112 /// A fatal error occurred and panicThreshold dictates it should throw.
113 Error {
114 error: CompilerErrorInfo,
115 events: Vec<LoggerEvent>,
116 #[serde(rename = "orderedLog", skip_serializing_if = "Vec::is_empty")]
117 ordered_log: Vec<OrderedLogItem>,
118 /// Timing data for profiling. Only populated when __profiling is enabled.
119 #[serde(skip_serializing_if = "Vec::is_empty")]
120 timing: Vec<TimingEntry>,
121 },
122 }
123
124 /// An item in the ordered log, which can be either a logger event or a debug entry.
125 #[derive(Debug, Clone, Serialize)]
126 #[serde(tag = "type", rename_all = "camelCase")]
127 pub enum OrderedLogItem {
128 Event { event: LoggerEvent },
129 Debug { entry: DebugLogEntry },
130 }
131
132 /// Structured error information for the JS shim.
133 #[derive(Debug, Clone, Serialize)]
134 pub struct CompilerErrorInfo {
135 pub reason: String,
136 #[serde(skip_serializing_if = "Option::is_none")]
137 pub description: Option<String>,
138 pub details: Vec<CompilerErrorDetailInfo>,
139 /// When set, the JS shim should throw an Error with this exact message
140 /// instead of formatting through formatCompilerError(). This is used
141 /// for simulated unknown exceptions (throwUnknownException__testonly)
142 /// which in the TS compiler are plain Error objects, not CompilerErrors.
143 #[serde(rename = "rawMessage", skip_serializing_if = "Option::is_none")]
144 pub raw_message: Option<String>,
145 /// Pre-formatted error message produced by Rust, matching the JS
146 /// formatCompilerError() output. When present, the JS shim uses this
147 /// directly instead of calling formatCompilerError() on the JS side.
148 #[serde(rename = "formattedMessage", skip_serializing_if = "Option::is_none")]
149 pub formatted_message: Option<String>,
150 }
151
152 /// Serializable error detail — flat plain object matching the TS
153 /// `formatDetailForLogging()` output. All fields are direct properties.
154 #[derive(Debug, Clone, Serialize)]
155 pub struct CompilerErrorDetailInfo {
156 pub category: String,
157 pub reason: String,
158 pub description: Option<String>,
159 pub severity: String,
160 pub suggestions: Option<Vec<LoggerSuggestionInfo>>,
161 #[serde(skip_serializing_if = "Option::is_none")]
162 pub details: Option<Vec<CompilerErrorItemInfo>>,
163 #[serde(skip_serializing_if = "Option::is_none")]
164 pub loc: Option<LoggerSourceLocation>,
165 }
166
167 /// Serializable suggestion info for logger events.
168 #[derive(Debug, Clone, Serialize)]
169 pub struct LoggerSuggestionInfo {
170 pub description: String,
171 pub op: LoggerSuggestionOp,
172 pub range: (usize, usize),
173 #[serde(skip_serializing_if = "Option::is_none")]
174 pub text: Option<String>,
175 }
176
177 /// Numeric enum matching TS `CompilerSuggestionOperation`.
178 #[derive(Debug, Clone, Copy)]
179 pub enum LoggerSuggestionOp {
180 InsertBefore = 0,
181 InsertAfter = 1,
182 Remove = 2,
183 Replace = 3,
184 }
185
186 impl serde::Serialize for LoggerSuggestionOp {
187 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
188 serializer.serialize_u8(*self as u8)
189 }
190 }
191
192 /// Individual error or hint item within a CompilerErrorDetailInfo.
193 #[derive(Debug, Clone, Serialize)]
194 pub struct CompilerErrorItemInfo {
195 pub kind: String,
196 pub loc: Option<LoggerSourceLocation>,
197 /// Serialized as `null` when None (not omitted), matching TS behavior.
198 pub message: Option<String>,
199 }
200
201 /// Debug log entry for debugLogIRs support.
202 /// Currently only supports the 'debug' variant (string values).
203 #[derive(Debug, Clone, Serialize)]
204 pub struct DebugLogEntry {
205 pub kind: &'static str,
206 pub name: String,
207 pub value: String,
208 }
209
210 impl DebugLogEntry {
211 pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
212 Self {
213 kind: "debug",
214 name: name.into(),
215 value: value.into(),
216 }
217 }
218 }
219
220 /// Codegen output for a single compiled function.
221 /// Carries the generated AST fields needed to replace the original function.
222 #[derive(Debug, Clone)]
223 pub struct CodegenFunction {
224 pub loc: Option<SourceLocation>,
225 pub id: Option<AstIdentifier>,
226 pub name_hint: Option<String>,
227 pub params: Vec<PatternLike>,
228 pub body: BlockStatement,
229 pub generator: bool,
230 pub is_async: bool,
231 pub memo_slots_used: u32,
232 pub memo_blocks: u32,
233 pub memo_values: u32,
234 pub pruned_memo_blocks: u32,
235 pub pruned_memo_values: u32,
236 pub outlined: Vec<OutlinedFunction>,
237 }
238
239 /// An outlined function extracted during compilation.
240 #[derive(Debug, Clone)]
241 pub struct OutlinedFunction {
242 pub func: CodegenFunction,
243 pub fn_type: Option<ReactFunctionType>,
244 }
245
246 /// Logger events emitted during compilation.
247 /// These are returned to JS for the logger callback.
248 #[derive(Debug, Clone, Serialize)]
249 #[serde(tag = "kind")]
250 pub enum LoggerEvent {
251 CompileSuccess {
252 #[serde(rename = "fnLoc")]
253 fn_loc: Option<LoggerSourceLocation>,
254 #[serde(rename = "fnName")]
255 fn_name: Option<String>,
256 #[serde(rename = "memoSlots")]
257 memo_slots: u32,
258 #[serde(rename = "memoBlocks")]
259 memo_blocks: u32,
260 #[serde(rename = "memoValues")]
261 memo_values: u32,
262 #[serde(rename = "prunedMemoBlocks")]
263 pruned_memo_blocks: u32,
264 #[serde(rename = "prunedMemoValues")]
265 pruned_memo_values: u32,
266 },
267 CompileError {
268 detail: CompilerErrorDetailInfo,
269 #[serde(rename = "fnLoc")]
270 fn_loc: Option<LoggerSourceLocation>,
271 },
272 /// Same as CompileError but serializes fnLoc before detail (matching TS program.ts output)
273 #[serde(rename = "CompileError")]
274 CompileErrorWithLoc {
275 #[serde(rename = "fnLoc")]
276 fn_loc: LoggerSourceLocation,
277 detail: CompilerErrorDetailInfo,
278 },
279 CompileSkip {
280 #[serde(rename = "fnLoc")]
281 fn_loc: Option<LoggerSourceLocation>,
282 reason: String,
283 #[serde(skip_serializing_if = "Option::is_none")]
284 loc: Option<LoggerSourceLocation>,
285 },
286 CompileUnexpectedThrow {
287 #[serde(rename = "fnLoc")]
288 fn_loc: Option<LoggerSourceLocation>,
289 data: String,
290 },
291 PipelineError {
292 #[serde(rename = "fnLoc")]
293 fn_loc: Option<LoggerSourceLocation>,
294 data: String,
295 },
296 }