@reckona/mreact-compiler 0.0.66 → 0.0.68

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/dist/compiler-module-context.js.map +1 -1
  2. package/dist/diagnostics.js.map +1 -1
  3. package/dist/emit-client.js.map +1 -1
  4. package/dist/emit-compat.js.map +1 -1
  5. package/dist/emit-escape-helper.js.map +1 -1
  6. package/dist/emit-server-shared.js.map +1 -1
  7. package/dist/emit-server-stream.js.map +1 -1
  8. package/dist/emit-server.js.map +1 -1
  9. package/dist/index.js.map +1 -1
  10. package/dist/internal.js.map +1 -1
  11. package/dist/ir.js.map +1 -1
  12. package/dist/oxc-analysis-types.js.map +1 -1
  13. package/dist/oxc-await-analysis.js.map +1 -1
  14. package/dist/oxc-await-ids.js.map +1 -1
  15. package/dist/oxc-await-validation.js.map +1 -1
  16. package/dist/oxc-bindings.js.map +1 -1
  17. package/dist/oxc-body-lowering.js.map +1 -1
  18. package/dist/oxc-child-analysis.js.map +1 -1
  19. package/dist/oxc-code-utils.js.map +1 -1
  20. package/dist/oxc-component-detection.js.map +1 -1
  21. package/dist/oxc-component-props.js.map +1 -1
  22. package/dist/oxc-component-references.js.map +1 -1
  23. package/dist/oxc-dom-lowering.js.map +1 -1
  24. package/dist/oxc-expression-utils.js.map +1 -1
  25. package/dist/oxc-jsx-attributes.js.map +1 -1
  26. package/dist/oxc-jsx-text.js.map +1 -1
  27. package/dist/oxc-nested-lowering.js.map +1 -1
  28. package/dist/oxc-node-utils.js.map +1 -1
  29. package/dist/oxc-raw-jsx.js.map +1 -1
  30. package/dist/oxc-render-values.js.map +1 -1
  31. package/dist/oxc-runtime-emit.js.map +1 -1
  32. package/dist/oxc-transform.js.map +1 -1
  33. package/dist/oxc.js.map +1 -1
  34. package/dist/transform.js.map +1 -1
  35. package/dist/types.js.map +1 -1
  36. package/package.json +4 -3
  37. package/src/compiler-module-context.ts +31 -0
  38. package/src/diagnostics.ts +184 -0
  39. package/src/emit-client.ts +837 -0
  40. package/src/emit-compat.ts +567 -0
  41. package/src/emit-escape-helper.ts +45 -0
  42. package/src/emit-server-shared.ts +384 -0
  43. package/src/emit-server-stream.ts +2558 -0
  44. package/src/emit-server.ts +1827 -0
  45. package/src/index.ts +44 -0
  46. package/src/internal.ts +1905 -0
  47. package/src/ir.ts +151 -0
  48. package/src/oxc-analysis-types.ts +5 -0
  49. package/src/oxc-await-analysis.ts +165 -0
  50. package/src/oxc-await-ids.ts +62 -0
  51. package/src/oxc-await-validation.ts +117 -0
  52. package/src/oxc-bindings.ts +70 -0
  53. package/src/oxc-body-lowering.ts +430 -0
  54. package/src/oxc-child-analysis.ts +791 -0
  55. package/src/oxc-code-utils.ts +19 -0
  56. package/src/oxc-component-detection.ts +459 -0
  57. package/src/oxc-component-props.ts +170 -0
  58. package/src/oxc-component-references.ts +613 -0
  59. package/src/oxc-dom-lowering.ts +127 -0
  60. package/src/oxc-expression-utils.ts +42 -0
  61. package/src/oxc-jsx-attributes.ts +110 -0
  62. package/src/oxc-jsx-text.ts +84 -0
  63. package/src/oxc-nested-lowering.ts +319 -0
  64. package/src/oxc-node-utils.ts +65 -0
  65. package/src/oxc-raw-jsx.ts +239 -0
  66. package/src/oxc-render-values.ts +620 -0
  67. package/src/oxc-runtime-emit.ts +212 -0
  68. package/src/oxc-transform.ts +77 -0
  69. package/src/oxc.ts +932 -0
  70. package/src/transform.ts +634 -0
  71. package/src/types.ts +117 -0
package/src/oxc.ts ADDED
@@ -0,0 +1,932 @@
1
+ import { parseSync } from "oxc-parser";
2
+ import { unsupportedTopLevelJsxInitializerDiagnostic } from "./diagnostics.js";
3
+ import {
4
+ type AnalyzeToIrInput,
5
+ type AnalyzeToIrOutput,
6
+ } from "./internal.js";
7
+ import {
8
+ createCompilerModuleContextWithOxc,
9
+ type CompilerModuleContext,
10
+ } from "./compiler-module-context.js";
11
+ import type { ClientReferenceIr, ComponentIr, ModuleIr } from "./ir.js";
12
+ import { transformJsxToCreateElementWithOxc } from "./oxc-transform.js";
13
+ import {
14
+ arraysEqual,
15
+ getOxcLocation,
16
+ getOxcLocationFromOffset,
17
+ readArray,
18
+ readObject,
19
+ readSource,
20
+ unwrapOxcParentheses,
21
+ } from "./oxc-node-utils.js";
22
+ import { assignOxcAwaitIds } from "./oxc-await-ids.js";
23
+ import {
24
+ validateOxcAwaitCompatComponents,
25
+ validateOxcNestedAwait,
26
+ } from "./oxc-await-validation.js";
27
+ import type { OxcBodyStatementJsxMode } from "./oxc-analysis-types.js";
28
+ import {
29
+ collectBindingNames,
30
+ collectImportBindingNames,
31
+ formatStatement,
32
+ readOxcParameterName,
33
+ } from "./oxc-bindings.js";
34
+ import {
35
+ formatOxcBodyStatement,
36
+ formatPreservedStatement,
37
+ lowerOxcBodyStatementJsx,
38
+ lowerOxcTopLevelStatement,
39
+ type OxcBodyLowerers,
40
+ } from "./oxc-body-lowering.js";
41
+ import { collectOxcVariableInitializers } from "./oxc-await-analysis.js";
42
+ import {
43
+ collectOxcAsyncComponentNames,
44
+ collectOxcExportedComponents,
45
+ collectOxcExportedFunctionNames,
46
+ collectOxcPlainComponentNames,
47
+ hasComponentReturn,
48
+ hasOxcFunctionLikeComponentReturn,
49
+ isOxcExportedFunctionLike,
50
+ isOxcComponentCallExpression,
51
+ isJsxRoot,
52
+ isOxcJsxComponentStatement,
53
+ isOxcUnsupportedExportedFunction,
54
+ readOxcPlainComponent,
55
+ readOxcVariableComponentDeclaration,
56
+ unwrapOxcComponentFunctionLikeInitializer,
57
+ } from "./oxc-component-detection.js";
58
+ import {
59
+ collectOxcClientBoundaryImportComponents,
60
+ collectOxcCompatRuntimeImportComponents,
61
+ markOxcAsyncComponentReferences,
62
+ markOxcClientReferences,
63
+ markOxcCompatRuntimeReferences,
64
+ } from "./oxc-component-references.js";
65
+ import { normalizeOxcExpressionCode, stripOxcGeneratedImports } from "./oxc-code-utils.js";
66
+ import {
67
+ analyzeOxcExpressionChild,
68
+ analyzeOxcJsxNode,
69
+ type OxcChildAnalysisContext,
70
+ } from "./oxc-child-analysis.js";
71
+ import { lowerOxcDomNodeExpression } from "./oxc-dom-lowering.js";
72
+ import {
73
+ lowerOxcCompatObjectExpression,
74
+ lowerOxcCompatReactNodeExpression,
75
+ lowerOxcNestedJsxExpression,
76
+ lowerOxcServerStringExpression,
77
+ } from "./oxc-nested-lowering.js";
78
+ import {
79
+ isOxcJsxBranch,
80
+ readOxcReturnExpressionFromStatement,
81
+ } from "./oxc-expression-utils.js";
82
+ import {
83
+ collectOxcBodyJsxBindingNames,
84
+ collectOxcReactiveReadAliases,
85
+ containsOxcJsxSyntax,
86
+ markOxcRenderValueExpressions,
87
+ rewriteOxcReactiveAliasExpressionCode,
88
+ } from "./oxc-render-values.js";
89
+ import { containsRawJsxInIr } from "./oxc-raw-jsx.js";
90
+ import type { AnalyzeModuleOptions, CompileTarget, Diagnostic } from "./types.js";
91
+
92
+ export interface OxcParityResult {
93
+ matches: boolean;
94
+ oxc: {
95
+ errors: string[];
96
+ exportedComponents: string[];
97
+ ir?: ModuleIr;
98
+ usedTypescriptFallback: boolean;
99
+ rawJsxDetected: boolean;
100
+ };
101
+ }
102
+
103
+ const oxcBodyLowerers: OxcBodyLowerers = createOxcBodyLowerers();
104
+
105
+ function createOxcBodyLowerers(
106
+ compatRuntimeImports: ReadonlyMap<string, ClientReferenceIr> = new Map(),
107
+ ): OxcBodyLowerers {
108
+ return {
109
+ lowerDomNodeExpression: lowerOxcDomNodeExpression,
110
+ lowerCompatObjectExpression: lowerOxcCompatObjectExpression,
111
+ lowerServerStringExpression: (code, expression, componentNames, target, diagnostics) =>
112
+ lowerOxcServerStringExpression(
113
+ code,
114
+ expression,
115
+ componentNames,
116
+ target,
117
+ diagnostics,
118
+ compatRuntimeImports,
119
+ ),
120
+ };
121
+ }
122
+
123
+ function createOxcChildAnalysisContext(
124
+ componentNames: Set<string>,
125
+ target: CompileTarget,
126
+ diagnostics: Diagnostic[],
127
+ bodyStatementJsx?: OxcBodyStatementJsxMode,
128
+ componentBodyBindings?: ReadonlyMap<string, Record<string, unknown>>,
129
+ reactiveAliasBindings?: ReadonlyMap<string, string>,
130
+ serverOutput?: AnalyzeModuleOptions["serverOutput"],
131
+ componentCallNames?: Set<string>,
132
+ bodyLowerers: OxcBodyLowerers = oxcBodyLowerers,
133
+ ): OxcChildAnalysisContext {
134
+ return {
135
+ componentNames,
136
+ ...(componentCallNames === undefined ? {} : { componentCallNames }),
137
+ target,
138
+ ...(serverOutput === undefined ? {} : { serverOutput }),
139
+ diagnostics,
140
+ ...(bodyStatementJsx === undefined ? {} : { bodyStatementJsx }),
141
+ ...(componentBodyBindings === undefined ? {} : { componentBodyBindings }),
142
+ ...(reactiveAliasBindings === undefined ? {} : { reactiveAliasBindings }),
143
+ bodyLowerers,
144
+ lowerNestedJsxExpression: lowerOxcNestedJsxExpression,
145
+ };
146
+ }
147
+
148
+ export function analyzeOxcParity(input: AnalyzeToIrInput): OxcParityResult {
149
+ const oxc = parseSync(input.filename, input.code, {
150
+ lang: "tsx",
151
+ sourceType: "module",
152
+ astType: "ts",
153
+ });
154
+ const oxcExportedComponents = collectOxcExportedComponents(oxc.program);
155
+ const oxcOutput = analyzeOxcToIr(input.code, oxc.program, input.target, input.options);
156
+ const rawJsxDetected = containsRawJsxInIr(oxcOutput.ir);
157
+
158
+ return {
159
+ matches:
160
+ oxc.errors.length === 0 &&
161
+ oxcOutput.diagnostics.length === 0 &&
162
+ !rawJsxDetected &&
163
+ arraysEqual(
164
+ oxcExportedComponents,
165
+ oxcOutput.ir.components
166
+ .filter((component) => component.exported !== false)
167
+ .map((component) => component.exportName),
168
+ ),
169
+ oxc: {
170
+ errors: oxc.errors.map((error) => error.message),
171
+ exportedComponents: oxcExportedComponents,
172
+ ir: oxcOutput.ir,
173
+ usedTypescriptFallback: false,
174
+ rawJsxDetected,
175
+ },
176
+ };
177
+ }
178
+
179
+ export function analyzeWithOxc(input: AnalyzeToIrInput): AnalyzeToIrOutput {
180
+ return analyzeCompilerModuleContextWithOxc(
181
+ createCompilerModuleContextWithOxc(input),
182
+ {
183
+ target: input.target,
184
+ ...(input.options === undefined ? {} : { options: input.options }),
185
+ },
186
+ );
187
+ }
188
+
189
+ export function analyzeCompilerModuleContextWithOxc(
190
+ context: CompilerModuleContext,
191
+ input: Omit<AnalyzeToIrInput, "code" | "filename">,
192
+ ): AnalyzeToIrOutput {
193
+ const analyzed = analyzeOxcToIr(context.code, context.program, input.target, input.options);
194
+
195
+ return {
196
+ ir: analyzed.ir,
197
+ diagnostics: [
198
+ ...context.parseErrors.map((error) => oxcParseErrorDiagnostic(context.code, error)),
199
+ ...analyzed.diagnostics,
200
+ ],
201
+ usedTypescriptFallback: false,
202
+ };
203
+ }
204
+
205
+ function oxcParseErrorDiagnostic(code: string, error: unknown): Diagnostic {
206
+ const object = readObject(error);
207
+ const firstLabel = readObject(readArray(object.labels)[0]);
208
+ const loc =
209
+ typeof firstLabel.start === "number"
210
+ ? getOxcLocationFromOffset(code, firstLabel.start)
211
+ : undefined;
212
+
213
+ return {
214
+ level: "error",
215
+ code:
216
+ object.message === "Unexpected token" ? "MR_INVALID_JSX_EXPRESSION" : "MR_OXC_PARSE_ERROR",
217
+ message: typeof object.message === "string" ? object.message : "Oxc parse error",
218
+ ...(loc === undefined ? {} : { loc }),
219
+ };
220
+ }
221
+
222
+ function analyzeOxcToIr(
223
+ code: string,
224
+ program: unknown,
225
+ target: CompileTarget,
226
+ options?: AnalyzeModuleOptions,
227
+ ): { ir: ModuleIr; diagnostics: Diagnostic[] } {
228
+ const body = readArray(readObject(program).body);
229
+ const userImports: string[] = [];
230
+ const moduleStatements: string[] = [];
231
+ const moduleBindingNames = new Set<string>();
232
+ const diagnostics: Diagnostic[] = [];
233
+ const clientBoundaryImports = collectOxcClientBoundaryImportComponents(
234
+ program,
235
+ new Set(options?.clientBoundaryImports ?? []),
236
+ );
237
+ const compatRuntimeImports = collectOxcCompatRuntimeImportComponents(program);
238
+ const bodyLowerers = createOxcBodyLowerers(compatRuntimeImports);
239
+ const moduleRenderValueBindings = collectOxcBodyJsxBindingNames(body);
240
+
241
+ for (const statement of body) {
242
+ const object = readObject(statement);
243
+
244
+ if (object.type === "ImportDeclaration") {
245
+ const importCode = formatStatement(code, statement);
246
+ const source = readObject(object.source).value;
247
+
248
+ if (importCode !== "" && !(target === "server" && isCssImportSource(source))) {
249
+ userImports.push(importCode);
250
+ }
251
+ for (const bindingName of collectImportBindingNames(statement)) {
252
+ moduleBindingNames.add(bindingName);
253
+ }
254
+ continue;
255
+ }
256
+
257
+ if (
258
+ isOxcJsxComponentStatement(statement) ||
259
+ (options?.compatReactNodeReturn === true && isOxcExportedFunctionLike(statement))
260
+ ) {
261
+ const declaration = readObject(readObject(statement).declaration);
262
+
263
+ if (declaration.type === "VariableDeclaration") {
264
+ for (const bindingName of collectBindingNames(declaration)) {
265
+ moduleBindingNames.add(bindingName);
266
+ }
267
+ }
268
+ continue;
269
+ } else {
270
+ if (isOxcUnsupportedExportedFunction(statement, options)) {
271
+ diagnostics.push({
272
+ level: "error",
273
+ code: "MR_UNSUPPORTED_COMPONENT_RETURN",
274
+ message: "Exported component must return a JSX element or supported React node.",
275
+ });
276
+ continue;
277
+ }
278
+ const loweredTopLevel = lowerOxcTopLevelStatement(
279
+ code,
280
+ statement,
281
+ componentNamesFromProgram(program, moduleBindingNames),
282
+ target,
283
+ diagnostics,
284
+ options,
285
+ bodyLowerers,
286
+ );
287
+ const formattedStatement =
288
+ loweredTopLevel ?? formatPreservedStatement(code, statement, options);
289
+
290
+ if (
291
+ loweredTopLevel === undefined &&
292
+ containsOxcJsxSyntax(object) &&
293
+ options?.topLevelJsx !== "compat-object" &&
294
+ options?.topLevelJsx !== "server-string"
295
+ ) {
296
+ diagnostics.push(
297
+ unsupportedTopLevelJsxInitializerDiagnostic(getOxcLocation(code, statement)),
298
+ );
299
+ }
300
+
301
+ if (formattedStatement !== "") {
302
+ moduleStatements.push(formattedStatement);
303
+ }
304
+ for (const bindingName of collectBindingNames(statement)) {
305
+ moduleBindingNames.add(bindingName);
306
+ }
307
+ }
308
+ }
309
+
310
+ const componentNames = componentNamesFromProgram(program, moduleBindingNames);
311
+ const componentCallNames =
312
+ options?.serverOutput === "stream" ? componentCallNamesFromProgram(program) : undefined;
313
+ const asyncComponentNames = collectOxcAsyncComponentNames(program);
314
+ const components = body.flatMap((statement) =>
315
+ analyzeOxcComponent(
316
+ code,
317
+ statement,
318
+ componentNames,
319
+ target,
320
+ diagnostics,
321
+ options?.bodyStatementJsx ?? "dom-node",
322
+ moduleRenderValueBindings,
323
+ options?.compatReactNodeReturn === true,
324
+ options?.serverOutput,
325
+ componentCallNames,
326
+ bodyLowerers,
327
+ ),
328
+ );
329
+
330
+ for (const component of components) {
331
+ markOxcAsyncComponentReferences(component.root, asyncComponentNames);
332
+ markOxcClientReferences(component.root, clientBoundaryImports);
333
+ markOxcCompatRuntimeReferences(component.root, compatRuntimeImports);
334
+ validateOxcNestedAwait(component.root, diagnostics);
335
+ validateOxcAwaitCompatComponents(component.root, diagnostics, {
336
+ allowCompatComponents: options?.awaitCompatComponents === "lower",
337
+ });
338
+ }
339
+
340
+ const ir: ModuleIr = {
341
+ userImports,
342
+ moduleStatements,
343
+ moduleBindingNames: Array.from(moduleBindingNames),
344
+ components,
345
+ };
346
+
347
+ assignOxcAwaitIds(ir);
348
+
349
+ return {
350
+ ir,
351
+ diagnostics,
352
+ };
353
+ }
354
+
355
+ function isCssImportSource(source: unknown): boolean {
356
+ return typeof source === "string" && /\.(?:css|pcss|postcss|scss|sass|less|styl|stylus)$/u.test(source);
357
+ }
358
+
359
+ function componentNamesFromProgram(
360
+ program: unknown,
361
+ moduleBindingNames: ReadonlySet<string>,
362
+ ): Set<string> {
363
+ return new Set([
364
+ ...collectOxcExportedComponents(program),
365
+ ...collectOxcExportedFunctionNames(program),
366
+ ...collectOxcPlainComponentNames(program),
367
+ ...moduleBindingNames,
368
+ ]);
369
+ }
370
+
371
+ function componentCallNamesFromProgram(program: unknown): Set<string> {
372
+ return new Set([
373
+ ...collectOxcExportedComponents(program).filter((name) => name !== "default"),
374
+ ...collectOxcPlainComponentNames(program),
375
+ ]);
376
+ }
377
+
378
+ function analyzeOxcComponent(
379
+ code: string,
380
+ statement: unknown,
381
+ componentNames: Set<string>,
382
+ target: CompileTarget,
383
+ diagnostics: Diagnostic[],
384
+ bodyStatementJsx: OxcBodyStatementJsxMode,
385
+ moduleRenderValueBindings: Set<string>,
386
+ compatReactNodeReturn: boolean,
387
+ serverOutput: AnalyzeModuleOptions["serverOutput"],
388
+ componentCallNames: Set<string> | undefined,
389
+ bodyLowerers: OxcBodyLowerers,
390
+ ): ComponentIr[] {
391
+ const object = readObject(statement);
392
+
393
+ if (object.type === "ExportDefaultDeclaration") {
394
+ const declaration = unwrapOxcComponentFunctionLikeInitializer(readObject(object.declaration));
395
+
396
+ if (declaration === undefined || !hasOxcFunctionLikeComponentReturn(declaration)) {
397
+ return [];
398
+ }
399
+ const id = readObject(declaration.id);
400
+ const name = typeof id.name === "string" ? id.name : "DefaultExport";
401
+
402
+ return [
403
+ analyzeOxcFunctionLikeComponent(
404
+ code,
405
+ name,
406
+ declaration,
407
+ "default",
408
+ componentNames,
409
+ target,
410
+ diagnostics,
411
+ bodyStatementJsx,
412
+ moduleRenderValueBindings,
413
+ compatReactNodeReturn,
414
+ serverOutput,
415
+ componentCallNames,
416
+ bodyLowerers,
417
+ true,
418
+ ),
419
+ ];
420
+ }
421
+
422
+ if (object.type !== "ExportNamedDeclaration") {
423
+ const plainComponent = readOxcPlainComponent(statement);
424
+
425
+ if (plainComponent === undefined) {
426
+ return [];
427
+ }
428
+
429
+ return [
430
+ {
431
+ ...analyzeOxcFunctionLikeComponent(
432
+ code,
433
+ plainComponent.name,
434
+ plainComponent.initializer,
435
+ plainComponent.name,
436
+ componentNames,
437
+ target,
438
+ diagnostics,
439
+ bodyStatementJsx,
440
+ moduleRenderValueBindings,
441
+ compatReactNodeReturn,
442
+ serverOutput,
443
+ componentCallNames,
444
+ bodyLowerers,
445
+ ),
446
+ exported: false,
447
+ },
448
+ ];
449
+ }
450
+
451
+ const declaration = readObject(object.declaration);
452
+
453
+ if (declaration.type === "VariableDeclaration") {
454
+ const variableComponent = readOxcVariableComponentDeclaration(declaration);
455
+
456
+ if (variableComponent === undefined) {
457
+ return [];
458
+ }
459
+
460
+ return [
461
+ analyzeOxcFunctionLikeComponent(
462
+ code,
463
+ variableComponent.name,
464
+ variableComponent.initializer,
465
+ variableComponent.name,
466
+ componentNames,
467
+ target,
468
+ diagnostics,
469
+ bodyStatementJsx,
470
+ moduleRenderValueBindings,
471
+ compatReactNodeReturn,
472
+ serverOutput,
473
+ componentCallNames,
474
+ bodyLowerers,
475
+ ),
476
+ ];
477
+ }
478
+
479
+ if (
480
+ declaration.type !== "FunctionDeclaration" ||
481
+ (!compatReactNodeReturn && !hasComponentReturn(declaration.body))
482
+ ) {
483
+ return [];
484
+ }
485
+
486
+ const id = readObject(declaration.id);
487
+
488
+ if (typeof id.name !== "string") {
489
+ return [];
490
+ }
491
+
492
+ return [
493
+ analyzeOxcFunctionLikeComponent(
494
+ code,
495
+ id.name,
496
+ declaration,
497
+ id.name,
498
+ componentNames,
499
+ target,
500
+ diagnostics,
501
+ bodyStatementJsx,
502
+ moduleRenderValueBindings,
503
+ compatReactNodeReturn,
504
+ serverOutput,
505
+ componentCallNames,
506
+ bodyLowerers,
507
+ ),
508
+ ];
509
+ }
510
+
511
+ function analyzeOxcFunctionLikeComponent(
512
+ code: string,
513
+ name: string,
514
+ functionLike: Record<string, unknown>,
515
+ exportName: string,
516
+ componentNames: Set<string>,
517
+ target: CompileTarget,
518
+ diagnostics: Diagnostic[],
519
+ bodyStatementJsx: OxcBodyStatementJsxMode,
520
+ moduleRenderValueBindings: Set<string>,
521
+ compatReactNodeReturn: boolean,
522
+ serverOutput: AnalyzeModuleOptions["serverOutput"],
523
+ componentCallNames: Set<string> | undefined,
524
+ bodyLowerers: OxcBodyLowerers,
525
+ exportDefault = false,
526
+ ): ComponentIr {
527
+ const functionBody = readObject(functionLike.body);
528
+ const body = functionBody.type === "BlockStatement" ? readArray(functionBody.body) : [];
529
+ const earlyIfRootReturn =
530
+ bodyStatementJsx === "compat-object" ? undefined : findOxcEarlyIfRootReturn(body);
531
+ const rootStatement =
532
+ earlyIfRootReturn?.branchStatements[0] ??
533
+ (bodyStatementJsx === "compat-object"
534
+ ? body.find((bodyStatement) => readObject(bodyStatement).type === "ReturnStatement")
535
+ : findOxcRootStatement(body));
536
+ const returnStatement =
537
+ readObject(rootStatement).type === "ReturnStatement" ? rootStatement : undefined;
538
+ const expressionBody = unwrapOxcParentheses(readObject(functionLike.body));
539
+ const returnExpression =
540
+ returnStatement === undefined
541
+ ? expressionBody
542
+ : unwrapOxcParentheses(readObject(readObject(returnStatement).argument));
543
+ const parameters = readArray(functionLike.params).map((param) =>
544
+ readOxcParameterName(code, param),
545
+ );
546
+ const bodyStatements = body
547
+ .filter(
548
+ (bodyStatement) =>
549
+ bodyStatement !== rootStatement &&
550
+ earlyIfRootReturn?.branchStatements.includes(bodyStatement) !== true &&
551
+ bodyStatement !== earlyIfRootReturn?.fallthroughStatement,
552
+ )
553
+ .map(
554
+ (bodyStatement) =>
555
+ lowerOxcBodyStatementJsx(
556
+ code,
557
+ bodyStatement,
558
+ componentNames,
559
+ target,
560
+ diagnostics,
561
+ bodyStatementJsx,
562
+ bodyLowerers,
563
+ ) ?? formatOxcBodyStatement(code, bodyStatement, bodyStatementJsx),
564
+ );
565
+ const componentBodyBindings = collectOxcVariableInitializers(body);
566
+ const reactiveAliasBindings = collectOxcReactiveReadAliases(code, body);
567
+ const childAnalysisContext = createOxcChildAnalysisContext(
568
+ componentNames,
569
+ target,
570
+ diagnostics,
571
+ bodyStatementJsx,
572
+ componentBodyBindings,
573
+ reactiveAliasBindings,
574
+ serverOutput,
575
+ componentCallNames,
576
+ bodyLowerers,
577
+ );
578
+ const root =
579
+ analyzeOxcEarlyIfRootReturn(
580
+ code,
581
+ earlyIfRootReturn,
582
+ childAnalysisContext,
583
+ bodyStatementJsx,
584
+ ) ??
585
+ analyzeOxcSwitchRootReturn(
586
+ code,
587
+ rootStatement,
588
+ childAnalysisContext,
589
+ bodyStatementJsx,
590
+ ) ??
591
+ (isJsxRoot(returnExpression.type) || returnExpression.type === "JSXFragment"
592
+ ? analyzeOxcJsxNode(code, returnExpression, childAnalysisContext)
593
+ : isOxcComponentCallExpression(returnExpression)
594
+ ? analyzeOxcComponentCallExpression(code, returnExpression)
595
+ : analyzeOxcDynamicRootReturn(
596
+ code,
597
+ returnExpression,
598
+ childAnalysisContext,
599
+ bodyStatementJsx,
600
+ ) ??
601
+ {
602
+ kind: "expr" as const,
603
+ code: normalizeOxcExpressionCode(
604
+ compatReactNodeReturn
605
+ ? (lowerOxcCompatReactNodeExpression(
606
+ code,
607
+ returnExpression,
608
+ componentNames,
609
+ target,
610
+ diagnostics,
611
+ ) ??
612
+ stripOxcGeneratedImports(
613
+ transformJsxToCreateElementWithOxc(readSource(code, returnExpression)),
614
+ ))
615
+ : readSource(code, returnExpression),
616
+ ),
617
+ ...(compatReactNodeReturn ? { renderMode: "react-node" as const } : {}),
618
+ });
619
+ markOxcRenderValueExpressions(
620
+ [root],
621
+ new Set([
622
+ ...moduleRenderValueBindings,
623
+ ...collectOxcBodyJsxBindingNames(
624
+ body.filter(
625
+ (bodyStatement) =>
626
+ bodyStatement !== rootStatement &&
627
+ earlyIfRootReturn?.branchStatements.includes(bodyStatement) !== true &&
628
+ bodyStatement !== earlyIfRootReturn?.fallthroughStatement,
629
+ ),
630
+ ),
631
+ ]),
632
+ bodyStatementJsx === "server-string" ? "html" : "dynamic",
633
+ );
634
+
635
+ return {
636
+ name,
637
+ exportName,
638
+ ...(exportDefault ? { exportDefault: true } : {}),
639
+ ...(functionLike.async === true ? { async: true } : {}),
640
+ parameters,
641
+ bodyStatements,
642
+ bindingNames: [...parameters, ...body.flatMap(collectBindingNames)],
643
+ root,
644
+ };
645
+ }
646
+
647
+ interface OxcEarlyIfRootReturn {
648
+ branchStatements: unknown[];
649
+ fallthroughStatement: unknown;
650
+ branches: Array<{
651
+ test: Record<string, unknown>;
652
+ consequent: Record<string, unknown>;
653
+ }>;
654
+ fallthrough: Record<string, unknown>;
655
+ }
656
+
657
+ function findOxcEarlyIfRootReturn(body: readonly unknown[]): OxcEarlyIfRootReturn | undefined {
658
+ for (let index = 0; index < body.length - 1; index += 1) {
659
+ const branches: OxcEarlyIfRootReturn["branches"] = [];
660
+ const branchStatements: unknown[] = [];
661
+ let cursor = index;
662
+
663
+ while (cursor < body.length - 1) {
664
+ const statement = readObject(body[cursor]);
665
+
666
+ if (statement.type !== "IfStatement" || readObject(statement.alternate).type !== undefined) {
667
+ break;
668
+ }
669
+
670
+ const consequent = readOxcPureReturnExpressionFromStatement(statement.consequent);
671
+
672
+ if (consequent === undefined || !isOxcRootReturnExpression(consequent)) {
673
+ break;
674
+ }
675
+
676
+ branches.push({
677
+ test: readObject(statement.test),
678
+ consequent,
679
+ });
680
+ branchStatements.push(body[cursor]);
681
+ cursor += 1;
682
+ }
683
+
684
+ if (branches.length === 0) {
685
+ continue;
686
+ }
687
+
688
+ const fallthroughStatement = body[cursor];
689
+ const fallthrough = readOxcReturnExpressionFromStatement(fallthroughStatement);
690
+
691
+ if (fallthrough === undefined || !isOxcRootReturnExpression(fallthrough)) {
692
+ continue;
693
+ }
694
+
695
+ return {
696
+ branchStatements,
697
+ fallthroughStatement,
698
+ branches,
699
+ fallthrough,
700
+ };
701
+ }
702
+
703
+ return undefined;
704
+ }
705
+
706
+ function readOxcPureReturnExpressionFromStatement(
707
+ statement: unknown,
708
+ ): Record<string, unknown> | undefined {
709
+ const object = readObject(statement);
710
+
711
+ if (object.type === "ReturnStatement") {
712
+ return readOxcReturnExpressionFromStatement(statement);
713
+ }
714
+
715
+ if (object.type !== "BlockStatement") {
716
+ return undefined;
717
+ }
718
+
719
+ const statements = readArray(object.body);
720
+ if (statements.length !== 1) {
721
+ return undefined;
722
+ }
723
+
724
+ return readOxcReturnExpressionFromStatement(statements[0]);
725
+ }
726
+
727
+ function isOxcRootReturnExpression(expression: Record<string, unknown>): boolean {
728
+ return isOxcEmptyRootReturn(expression) || isOxcRenderableRootReturn(expression);
729
+ }
730
+
731
+ function isOxcEmptyRootReturn(expression: Record<string, unknown>): boolean {
732
+ return (
733
+ expression.type === "Literal" &&
734
+ (expression.value === null || expression.value === false)
735
+ );
736
+ }
737
+
738
+ function isOxcRenderableRootReturn(expression: Record<string, unknown>): boolean {
739
+ return (
740
+ isJsxRoot(expression.type) ||
741
+ expression.type === "JSXFragment" ||
742
+ isOxcComponentCallExpression(expression) ||
743
+ analyzeOxcDynamicRootReturnShape(expression)
744
+ );
745
+ }
746
+
747
+ function analyzeOxcDynamicRootReturnShape(expression: Record<string, unknown>): boolean {
748
+ if (expression.type === "ConditionalExpression") {
749
+ return true;
750
+ }
751
+
752
+ return (
753
+ expression.type === "LogicalExpression" &&
754
+ isOxcJsxBranch(readObject(expression.right))
755
+ );
756
+ }
757
+
758
+ function findOxcRootStatement(body: readonly unknown[]): unknown | undefined {
759
+ return body.find((bodyStatement) => {
760
+ const object = readObject(bodyStatement);
761
+ return object.type === "ReturnStatement" || isOxcSwitchRootReturnStatement(object);
762
+ });
763
+ }
764
+
765
+ function isOxcSwitchRootReturnStatement(statement: Record<string, unknown>): boolean {
766
+ if (statement.type !== "SwitchStatement") {
767
+ return false;
768
+ }
769
+
770
+ return readArray(statement.cases).some((switchCase) =>
771
+ readArray(readObject(switchCase).consequent).some(
772
+ (child) => readObject(child).type === "ReturnStatement",
773
+ ),
774
+ );
775
+ }
776
+
777
+ function analyzeOxcSwitchRootReturn(
778
+ code: string,
779
+ statement: unknown,
780
+ context: OxcChildAnalysisContext,
781
+ bodyStatementJsx: OxcBodyStatementJsxMode,
782
+ ): ComponentIr["root"] | undefined {
783
+ const object = readObject(statement);
784
+
785
+ if (object.type !== "SwitchStatement") {
786
+ return undefined;
787
+ }
788
+
789
+ const discriminant = readSource(code, object.discriminant);
790
+ const cases = readArray(object.cases).map((switchCase) => {
791
+ const caseObject = readObject(switchCase);
792
+ const returnStatement = readArray(caseObject.consequent)
793
+ .map(readObject)
794
+ .find((child) => child.type === "ReturnStatement");
795
+ const argument =
796
+ returnStatement === undefined
797
+ ? undefined
798
+ : unwrapOxcParentheses(readObject(returnStatement.argument));
799
+
800
+ return {
801
+ test: readObject(caseObject.test),
802
+ children:
803
+ argument === undefined
804
+ ? undefined
805
+ : analyzeOxcDynamicRootBranch(code, argument, context, bodyStatementJsx),
806
+ };
807
+ });
808
+
809
+ if (cases.some((entry) => entry.children === undefined)) {
810
+ return undefined;
811
+ }
812
+
813
+ const defaultCase = cases.find((entry) => entry.test.type === undefined);
814
+ let fallback = defaultCase?.children ?? [];
815
+
816
+ for (const entry of [...cases].reverse()) {
817
+ if (entry.test.type === undefined) {
818
+ continue;
819
+ }
820
+
821
+ fallback = [
822
+ {
823
+ kind: "conditional",
824
+ conditionCode: `${discriminant} === ${readSource(code, entry.test)}`,
825
+ whenTrue: entry.children ?? [],
826
+ whenFalse: fallback,
827
+ },
828
+ ];
829
+ }
830
+
831
+ return fallback.length === 1 ? fallback[0] : { kind: "fragment", children: fallback };
832
+ }
833
+
834
+ function analyzeOxcEarlyIfRootReturn(
835
+ code: string,
836
+ earlyIfRootReturn: OxcEarlyIfRootReturn | undefined,
837
+ context: OxcChildAnalysisContext,
838
+ bodyStatementJsx: OxcBodyStatementJsxMode,
839
+ ): ComponentIr["root"] | undefined {
840
+ if (earlyIfRootReturn === undefined) {
841
+ return undefined;
842
+ }
843
+
844
+ let fallback = analyzeOxcDynamicRootBranch(
845
+ code,
846
+ earlyIfRootReturn.fallthrough,
847
+ context,
848
+ bodyStatementJsx,
849
+ );
850
+
851
+ for (const branch of [...earlyIfRootReturn.branches].reverse()) {
852
+ fallback = [
853
+ {
854
+ kind: "conditional",
855
+ conditionCode: readOxcReactiveRootConditionCode(code, branch.test, context),
856
+ whenTrue: analyzeOxcDynamicRootBranch(
857
+ code,
858
+ branch.consequent,
859
+ context,
860
+ bodyStatementJsx,
861
+ ),
862
+ whenFalse: fallback,
863
+ },
864
+ ];
865
+ }
866
+
867
+ return fallback.length === 1 ? fallback[0] : { kind: "fragment", children: fallback };
868
+ }
869
+
870
+ function readOxcReactiveRootConditionCode(
871
+ code: string,
872
+ expression: Record<string, unknown>,
873
+ context: OxcChildAnalysisContext,
874
+ ): string {
875
+ return (
876
+ rewriteOxcReactiveAliasExpressionCode(code, expression, context.reactiveAliasBindings) ??
877
+ readSource(code, expression)
878
+ );
879
+ }
880
+
881
+ function analyzeOxcDynamicRootBranch(
882
+ code: string,
883
+ expression: Record<string, unknown>,
884
+ context: OxcChildAnalysisContext,
885
+ bodyStatementJsx: OxcBodyStatementJsxMode,
886
+ ): ComponentIr["root"][] {
887
+ if (expression.type === "Literal" && (expression.value === null || expression.value === false)) {
888
+ return [];
889
+ }
890
+
891
+ return analyzeOxcExpressionChild(code, expression, context, bodyStatementJsx);
892
+ }
893
+
894
+ function analyzeOxcDynamicRootReturn(
895
+ code: string,
896
+ returnExpression: Record<string, unknown>,
897
+ context: OxcChildAnalysisContext,
898
+ bodyStatementJsx: OxcBodyStatementJsxMode,
899
+ ): ComponentIr["root"] | undefined {
900
+ const nodes = analyzeOxcExpressionChild(code, returnExpression, context, bodyStatementJsx);
901
+
902
+ if (nodes.length !== 1) {
903
+ return undefined;
904
+ }
905
+
906
+ const [root] = nodes;
907
+ return root?.kind === "conditional" ? root : undefined;
908
+ }
909
+
910
+ function analyzeOxcComponentCallExpression(
911
+ code: string,
912
+ expression: Record<string, unknown>,
913
+ ): ComponentIr["root"] {
914
+ const callee = readObject(expression.callee);
915
+ const args = readArray(expression.arguments);
916
+ const firstArg = readObject(args[0]);
917
+
918
+ return {
919
+ kind: "component",
920
+ name: readSource(code, callee),
921
+ props:
922
+ firstArg.type === undefined
923
+ ? []
924
+ : [
925
+ {
926
+ kind: "spread-prop" as const,
927
+ code: normalizeOxcExpressionCode(readSource(code, firstArg)),
928
+ },
929
+ ],
930
+ children: [],
931
+ };
932
+ }