@reckona/mreact-compiler 0.0.65 → 0.0.67

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.
@@ -0,0 +1,791 @@
1
+ import {
2
+ invalidJsxExpressionDiagnostic,
3
+ unserializableAwaitValueDiagnostic,
4
+ unsupportedComponentReferenceDiagnostic,
5
+ } from "./diagnostics.js";
6
+ import type { AsyncBoundaryIr, JsxElementIr, JsxNodeIr } from "./ir.js";
7
+ import type { OxcBodyStatementJsxMode } from "./oxc-analysis-types.js";
8
+ import {
9
+ detectUnserializableAwaitValueReason,
10
+ readOxcExpressionAttribute,
11
+ readOxcExpressionAttributeNode,
12
+ } from "./oxc-await-analysis.js";
13
+ import {
14
+ analyzeOxcArrowJsxRenderer,
15
+ analyzeOxcComponentProp,
16
+ analyzeOxcSingleArrowJsxChild,
17
+ readOxcConsumerRenderProp,
18
+ } from "./oxc-component-props.js";
19
+ import {
20
+ formatOxcBodyStatement,
21
+ lowerOxcBodyStatementJsx,
22
+ type OxcBodyLowerers,
23
+ } from "./oxc-body-lowering.js";
24
+ import { normalizeOxcExpressionCode, stripOxcGeneratedImports } from "./oxc-code-utils.js";
25
+ import {
26
+ findOxcKeyCodeInChildren,
27
+ isOxcJsxBranch,
28
+ readOxcReturnExpressionFromStatement,
29
+ } from "./oxc-expression-utils.js";
30
+ import {
31
+ analyzeOxcAttribute,
32
+ findOxcJsxAttributeCode,
33
+ readOxcJsxTagName,
34
+ } from "./oxc-jsx-attributes.js";
35
+ import { normalizeOxcJsxText } from "./oxc-jsx-text.js";
36
+ import {
37
+ getOxcLocation,
38
+ readArray,
39
+ readObject,
40
+ readSource,
41
+ unwrapOxcParentheses,
42
+ } from "./oxc-node-utils.js";
43
+ import {
44
+ collectOxcBodyJsxBindingNames,
45
+ containsOxcJsxSyntax,
46
+ isOxcRenderValueExpression,
47
+ markOxcRenderValueExpressions,
48
+ rewriteOxcReactiveAliasExpressionCode,
49
+ } from "./oxc-render-values.js";
50
+ import { transformJsxWithOxc } from "./oxc-transform.js";
51
+ import type { CompileTarget, Diagnostic, ServerOutputMode } from "./types.js";
52
+
53
+ export interface OxcChildAnalysisContext {
54
+ componentNames: Set<string>;
55
+ componentCallNames?: Set<string>;
56
+ target: CompileTarget;
57
+ serverOutput?: ServerOutputMode;
58
+ diagnostics: Diagnostic[];
59
+ bodyStatementJsx?: OxcBodyStatementJsxMode;
60
+ componentBodyBindings?: ReadonlyMap<string, Record<string, unknown>>;
61
+ reactiveAliasBindings?: ReadonlyMap<string, string>;
62
+ bodyLowerers: OxcBodyLowerers;
63
+ lowerNestedJsxExpression: (
64
+ code: string,
65
+ expression: Record<string, unknown>,
66
+ componentNames: Set<string>,
67
+ target: CompileTarget,
68
+ diagnostics: Diagnostic[],
69
+ bodyStatementJsx: OxcBodyStatementJsxMode,
70
+ ) => string | undefined;
71
+ }
72
+
73
+ export function analyzeOxcJsxNode(
74
+ code: string,
75
+ node: Record<string, unknown>,
76
+ context: OxcChildAnalysisContext,
77
+ bodyStatementJsx = resolveOxcBodyStatementJsx(context),
78
+ ): JsxNodeIr {
79
+ if (node.type === "JSXFragment") {
80
+ return {
81
+ kind: "fragment",
82
+ children: analyzeOxcChildren(
83
+ code,
84
+ readArray(node.children),
85
+ context,
86
+ bodyStatementJsx,
87
+ ),
88
+ };
89
+ }
90
+
91
+ if (node.type !== "JSXElement") {
92
+ return { kind: "expr", code: readSource(code, node) };
93
+ }
94
+
95
+ const openingElement = readObject(node.openingElement);
96
+ const tagName = readOxcJsxTagName(readObject(openingElement.name));
97
+ const attributes = readArray(openingElement.attributes);
98
+
99
+ if (tagName === "Await") {
100
+ return analyzeOxcAsyncBoundary(code, node, attributes, context, bodyStatementJsx);
101
+ }
102
+
103
+ if (tagName === "Slot") {
104
+ const keyCode = findOxcJsxAttributeCode(code, attributes, "key");
105
+ const allowRef = bodyStatementJsx === "compat-object";
106
+
107
+ return {
108
+ kind: "element",
109
+ tagName: "slot",
110
+ ...(keyCode === undefined ? {} : { keyCode }),
111
+ attributes: attributes
112
+ .flatMap((attr) =>
113
+ analyzeOxcAttribute(code, attr, context.target, context.diagnostics, {
114
+ allowRef,
115
+ resolveExpressionCode: (expression) =>
116
+ readOxcReactiveExpressionCode(code, expression, context),
117
+ }),
118
+ )
119
+ .filter((attribute) => attribute.kind === "spread-attr" || attribute.name !== "key"),
120
+ children: analyzeOxcChildren(
121
+ code,
122
+ readArray(node.children),
123
+ context,
124
+ bodyStatementJsx,
125
+ ),
126
+ } satisfies JsxElementIr;
127
+ }
128
+
129
+ if (
130
+ /^[A-Z][\w$]*(?:\.[A-Za-z_$][\w$]*)+$/.test(tagName) ||
131
+ context.componentNames.has(tagName) ||
132
+ isOxcServerRuntimeComponentBinding(tagName, context)
133
+ ) {
134
+ const keyCode = findOxcJsxAttributeCode(code, attributes, "key");
135
+ const allowRef = bodyStatementJsx === "compat-object";
136
+ const analyzeJsxNode = (
137
+ child: Record<string, unknown>,
138
+ childBodyStatementJsx: OxcBodyStatementJsxMode = bodyStatementJsx,
139
+ ) => analyzeOxcJsxNode(code, child, context, childBodyStatementJsx);
140
+ const consumerRenderProp = tagName.endsWith(".Consumer")
141
+ ? readOxcConsumerRenderProp(
142
+ code,
143
+ readArray(node.children),
144
+ analyzeJsxNode,
145
+ bodyStatementJsx,
146
+ )
147
+ : undefined;
148
+ const componentLoc = getOxcLocation(code, openingElement.name);
149
+
150
+ return {
151
+ kind: "component",
152
+ name: tagName,
153
+ ...(componentLoc === undefined ? {} : { loc: componentLoc }),
154
+ ...(keyCode === undefined ? {} : { keyCode }),
155
+ props: attributes
156
+ .flatMap((attr) =>
157
+ analyzeOxcComponentProp(code, attr, analyzeJsxNode, context.diagnostics, {
158
+ allowRef,
159
+ resolveExpressionCode: (expression) =>
160
+ readOxcReactiveExpressionCode(code, expression, context),
161
+ }),
162
+ )
163
+ .filter((prop) => prop.kind === "spread-prop" || prop.name !== "key")
164
+ .concat(consumerRenderProp === undefined ? [] : [consumerRenderProp]),
165
+ children:
166
+ consumerRenderProp === undefined
167
+ ? analyzeOxcChildren(code, readArray(node.children), context, bodyStatementJsx)
168
+ : [],
169
+ };
170
+ }
171
+
172
+ if (/^[A-Z]/.test(tagName)) {
173
+ const componentLoc = getOxcLocation(code, openingElement.name);
174
+ context.diagnostics.push(unsupportedComponentReferenceDiagnostic(tagName, componentLoc));
175
+
176
+ return {
177
+ kind: "component",
178
+ name: tagName,
179
+ ...(componentLoc === undefined ? {} : { loc: componentLoc }),
180
+ props: [],
181
+ children: [],
182
+ };
183
+ }
184
+
185
+ const keyCode = findOxcJsxAttributeCode(code, attributes, "key");
186
+ const allowRef = bodyStatementJsx === "compat-object";
187
+
188
+ return {
189
+ kind: "element",
190
+ tagName,
191
+ ...(keyCode === undefined ? {} : { keyCode }),
192
+ attributes: attributes
193
+ .flatMap((attr) =>
194
+ analyzeOxcAttribute(code, attr, context.target, context.diagnostics, {
195
+ allowRef,
196
+ resolveExpressionCode: (expression) =>
197
+ readOxcReactiveExpressionCode(code, expression, context),
198
+ }),
199
+ )
200
+ .filter((attribute) => attribute.kind === "spread-attr" || attribute.name !== "key"),
201
+ children: analyzeOxcChildren(code, readArray(node.children), context, bodyStatementJsx),
202
+ } satisfies JsxElementIr;
203
+ }
204
+
205
+ function isOxcServerRuntimeComponentBinding(
206
+ tagName: string,
207
+ context: OxcChildAnalysisContext,
208
+ ): boolean {
209
+ if (context.target !== "server" || !/^[A-Z]/.test(tagName)) {
210
+ return false;
211
+ }
212
+
213
+ const binding = context.componentBodyBindings?.get(tagName);
214
+ if (binding === undefined) {
215
+ return false;
216
+ }
217
+
218
+ return isOxcRuntimeComponentExpression(binding);
219
+ }
220
+
221
+ function isOxcRuntimeComponentExpression(expression: Record<string, unknown>): boolean {
222
+ if (
223
+ expression.type === "Identifier" ||
224
+ expression.type === "MemberExpression" ||
225
+ expression.type === "CallExpression" ||
226
+ expression.type === "FunctionExpression" ||
227
+ expression.type === "ArrowFunctionExpression"
228
+ ) {
229
+ return true;
230
+ }
231
+
232
+ if (expression.type === "ChainExpression") {
233
+ return isOxcRuntimeComponentExpression(readObject(expression.expression));
234
+ }
235
+
236
+ if (expression.type === "ConditionalExpression") {
237
+ return (
238
+ isOxcRuntimeComponentExpression(readObject(expression.consequent)) &&
239
+ isOxcRuntimeComponentExpression(readObject(expression.alternate))
240
+ );
241
+ }
242
+
243
+ if (expression.type === "LogicalExpression") {
244
+ return isOxcRuntimeComponentExpression(readObject(expression.right));
245
+ }
246
+
247
+ return false;
248
+ }
249
+
250
+ function analyzeOxcAsyncBoundary(
251
+ code: string,
252
+ node: Record<string, unknown>,
253
+ attributes: readonly unknown[],
254
+ context: OxcChildAnalysisContext,
255
+ bodyStatementJsx: OxcBodyStatementJsxMode,
256
+ ): AsyncBoundaryIr {
257
+ const valueExpression = readOxcExpressionAttributeNode(attributes, "value");
258
+
259
+ if (valueExpression !== undefined) {
260
+ const unserializableReason = detectUnserializableAwaitValueReason(
261
+ valueExpression,
262
+ context.componentBodyBindings,
263
+ );
264
+
265
+ if (unserializableReason !== undefined) {
266
+ context.diagnostics.push(unserializableAwaitValueDiagnostic(unserializableReason));
267
+ }
268
+ }
269
+
270
+ const valueCode = readOxcExpressionAttribute(code, attributes, "value") ?? "undefined";
271
+ const placeholderExpression = readOxcExpressionAttributeNode(attributes, "placeholder");
272
+ const placeholderTagCode = findOxcJsxAttributeCode(code, attributes, "placeholderAs");
273
+ const catchExpression = readOxcExpressionAttributeNode(attributes, "catch");
274
+ const renderer = analyzeOxcSingleArrowJsxChild(
275
+ code,
276
+ readArray(node.children),
277
+ (child, childBodyStatementJsx = bodyStatementJsx) =>
278
+ analyzeOxcJsxNode(code, child, context, childBodyStatementJsx),
279
+ bodyStatementJsx,
280
+ );
281
+ const catchRenderer =
282
+ catchExpression !== undefined && readObject(catchExpression).type === "ArrowFunctionExpression"
283
+ ? analyzeOxcArrowJsxRenderer(
284
+ code,
285
+ readObject(catchExpression),
286
+ (child, childBodyStatementJsx = bodyStatementJsx) =>
287
+ analyzeOxcJsxNode(code, child, context, childBodyStatementJsx),
288
+ bodyStatementJsx,
289
+ )
290
+ : undefined;
291
+ const placeholderChildren =
292
+ placeholderExpression === undefined
293
+ ? undefined
294
+ : analyzeOxcExpressionChild(
295
+ code,
296
+ readObject(placeholderExpression),
297
+ context,
298
+ bodyStatementJsx,
299
+ );
300
+ const openingElement = readObject(node.openingElement);
301
+ const awaitLoc = getOxcLocation(code, readObject(openingElement.name));
302
+
303
+ return {
304
+ kind: "async-boundary",
305
+ ...(awaitLoc === undefined ? {} : { loc: awaitLoc }),
306
+ valueCode,
307
+ valueName: renderer.valueName,
308
+ children: renderer.children,
309
+ ...(placeholderChildren === undefined ? {} : { placeholderChildren }),
310
+ ...(placeholderTagCode === undefined ? {} : { placeholderTagCode }),
311
+ ...(catchRenderer === undefined
312
+ ? {}
313
+ : {
314
+ catchName: catchRenderer.valueName,
315
+ catchChildren: catchRenderer.children,
316
+ }),
317
+ };
318
+ }
319
+
320
+ export function analyzeOxcChildren(
321
+ code: string,
322
+ children: readonly unknown[],
323
+ context: OxcChildAnalysisContext,
324
+ bodyStatementJsx = resolveOxcBodyStatementJsx(context),
325
+ ): JsxNodeIr[] {
326
+ return children.flatMap((child, index): JsxNodeIr[] => {
327
+ const object = readObject(child);
328
+
329
+ if (object.type === "JSXText") {
330
+ const value = typeof object.value === "string" ? object.value : "";
331
+ const normalizedValue = normalizeOxcJsxText(value, children, index);
332
+ return normalizedValue === "" ? [] : [{ kind: "text", value: normalizedValue }];
333
+ }
334
+
335
+ if (object.type === "JSXElement" || object.type === "JSXFragment") {
336
+ return [analyzeOxcJsxNode(code, object, context, bodyStatementJsx)];
337
+ }
338
+
339
+ if (object.type === "JSXExpressionContainer") {
340
+ return analyzeOxcExpressionChild(
341
+ code,
342
+ readObject(object.expression),
343
+ context,
344
+ bodyStatementJsx,
345
+ );
346
+ }
347
+
348
+ return [];
349
+ });
350
+ }
351
+
352
+ export function analyzeOxcExpressionChild(
353
+ code: string,
354
+ expression: Record<string, unknown>,
355
+ context: OxcChildAnalysisContext,
356
+ bodyStatementJsx = resolveOxcBodyStatementJsx(context),
357
+ ): JsxNodeIr[] {
358
+ const unwrappedExpression = unwrapOxcParentheses(expression);
359
+
360
+ if (unwrappedExpression.type === "JSXEmptyExpression") {
361
+ context.diagnostics.push(invalidJsxExpressionDiagnostic(getOxcLocation(code, expression), "text"));
362
+ return [];
363
+ }
364
+
365
+ if (unwrappedExpression.type === "ConditionalExpression") {
366
+ return [
367
+ {
368
+ kind: "conditional",
369
+ conditionCode: readOxcReactiveExpressionCode(
370
+ code,
371
+ readObject(unwrappedExpression.test),
372
+ context,
373
+ ),
374
+ whenTrue: analyzeOxcDynamicBranch(
375
+ code,
376
+ readObject(unwrappedExpression.consequent),
377
+ context,
378
+ bodyStatementJsx,
379
+ ),
380
+ whenFalse: analyzeOxcDynamicBranch(
381
+ code,
382
+ readObject(unwrappedExpression.alternate),
383
+ context,
384
+ bodyStatementJsx,
385
+ ),
386
+ },
387
+ ];
388
+ }
389
+
390
+ if (
391
+ unwrappedExpression.type === "LogicalExpression" &&
392
+ isOxcJsxBranch(readObject(unwrappedExpression.right))
393
+ ) {
394
+ const rightBranch = analyzeOxcDynamicBranch(
395
+ code,
396
+ readObject(unwrappedExpression.right),
397
+ context,
398
+ bodyStatementJsx,
399
+ );
400
+
401
+ if (unwrappedExpression.operator === "&&") {
402
+ return [
403
+ {
404
+ kind: "conditional",
405
+ conditionCode: readOxcReactiveExpressionCode(
406
+ code,
407
+ readObject(unwrappedExpression.left),
408
+ context,
409
+ ),
410
+ whenTrue: rightBranch,
411
+ whenFalse: [],
412
+ },
413
+ ];
414
+ }
415
+
416
+ if (unwrappedExpression.operator === "||") {
417
+ return [
418
+ {
419
+ kind: "conditional",
420
+ conditionCode: readOxcReactiveExpressionCode(
421
+ code,
422
+ readObject(unwrappedExpression.left),
423
+ context,
424
+ ),
425
+ whenTrue: [
426
+ {
427
+ kind: "expr",
428
+ code: readOxcReactiveExpressionCode(
429
+ code,
430
+ readObject(unwrappedExpression.left),
431
+ context,
432
+ ),
433
+ },
434
+ ],
435
+ whenFalse: rightBranch,
436
+ },
437
+ ];
438
+ }
439
+ }
440
+
441
+ const list = analyzeOxcListExpression(code, unwrappedExpression, context, bodyStatementJsx);
442
+
443
+ if (list !== undefined) {
444
+ return [list];
445
+ }
446
+
447
+ if (unwrappedExpression.type === "JSXElement" || unwrappedExpression.type === "JSXFragment") {
448
+ return [analyzeOxcJsxNode(code, unwrappedExpression, context, bodyStatementJsx)];
449
+ }
450
+
451
+ const sameModuleComponentStreamCall =
452
+ context.target === "server" && context.serverOutput === "stream"
453
+ ? emitOxcSameModuleComponentStreamCall(
454
+ code,
455
+ expression,
456
+ context.componentCallNames ?? context.componentNames,
457
+ )
458
+ : undefined;
459
+ const componentCallNamesForRenderMode =
460
+ context.target === "server" && context.serverOutput === "stream"
461
+ ? context.componentCallNames ?? context.componentNames
462
+ : context.componentNames;
463
+ const sameModuleComponentCall =
464
+ sameModuleComponentStreamCall !== undefined ||
465
+ isOxcSameModuleComponentCallExpression(expression, componentCallNamesForRenderMode);
466
+
467
+ return [
468
+ {
469
+ kind: "expr",
470
+ code:
471
+ sameModuleComponentStreamCall ??
472
+ (containsOxcJsxSyntax(unwrappedExpression)
473
+ ? normalizeOxcExpressionCode(
474
+ context.lowerNestedJsxExpression(
475
+ code,
476
+ expression,
477
+ context.componentNames,
478
+ context.target,
479
+ context.diagnostics,
480
+ bodyStatementJsx,
481
+ ) ??
482
+ (bodyStatementJsx === "compat-object"
483
+ ? stripOxcGeneratedImports(transformJsxWithOxc(readSource(code, expression)))
484
+ : readOxcReactiveExpressionCode(code, expression, context)),
485
+ )
486
+ : readOxcReactiveExpressionCode(code, expression, context)),
487
+ ...(isOxcRenderValueExpression(expression) || sameModuleComponentCall
488
+ ? {
489
+ renderMode:
490
+ sameModuleComponentStreamCall !== undefined
491
+ ? ("stream-node" as const)
492
+ : bodyStatementJsx === "server-string"
493
+ ? ("html" as const)
494
+ : ("dynamic" as const),
495
+ }
496
+ : {}),
497
+ },
498
+ ];
499
+ }
500
+
501
+ function readOxcReactiveExpressionCode(
502
+ code: string,
503
+ expression: Record<string, unknown>,
504
+ context: OxcChildAnalysisContext,
505
+ ): string {
506
+ const unwrappedExpression = unwrapOxcParentheses(expression);
507
+
508
+ if (unwrappedExpression.type === "Identifier" && typeof unwrappedExpression.name === "string") {
509
+ return context.reactiveAliasBindings?.get(unwrappedExpression.name) ?? readSource(code, expression);
510
+ }
511
+
512
+ return (
513
+ rewriteOxcReactiveAliasExpressionCode(code, expression, context.reactiveAliasBindings) ??
514
+ readSource(code, expression)
515
+ );
516
+ }
517
+
518
+ function isOxcSameModuleComponentCallExpression(
519
+ expression: Record<string, unknown>,
520
+ componentNames: ReadonlySet<string>,
521
+ ): boolean {
522
+ const unwrappedExpression = unwrapOxcParentheses(expression);
523
+
524
+ if (unwrappedExpression.type !== "CallExpression") {
525
+ return false;
526
+ }
527
+
528
+ const callee = readObject(unwrappedExpression.callee);
529
+
530
+ return (
531
+ callee.type === "Identifier" &&
532
+ typeof callee.name === "string" &&
533
+ componentNames.has(callee.name)
534
+ );
535
+ }
536
+
537
+ function emitOxcSameModuleComponentStreamCall(
538
+ code: string,
539
+ expression: Record<string, unknown>,
540
+ componentNames: ReadonlySet<string>,
541
+ ): string | undefined {
542
+ const unwrappedExpression = unwrapOxcParentheses(expression);
543
+
544
+ if (unwrappedExpression.type !== "CallExpression") {
545
+ return undefined;
546
+ }
547
+
548
+ const callee = readObject(unwrappedExpression.callee);
549
+
550
+ if (
551
+ callee.type !== "Identifier" ||
552
+ typeof callee.name !== "string" ||
553
+ !componentNames.has(callee.name)
554
+ ) {
555
+ return undefined;
556
+ }
557
+
558
+ const args = readArray(unwrappedExpression.arguments)
559
+ .map((argument) => readSource(code, argument))
560
+ .join(", ");
561
+
562
+ return `($sink) => ${callee.name}($sink${args === "" ? "" : `, ${args}`})`;
563
+ }
564
+
565
+ function analyzeOxcDynamicBranch(
566
+ code: string,
567
+ expression: Record<string, unknown>,
568
+ context: OxcChildAnalysisContext,
569
+ bodyStatementJsx: OxcBodyStatementJsxMode,
570
+ ): JsxNodeIr[] {
571
+ if (expression.type === "Literal" && (expression.value === null || expression.value === false)) {
572
+ return [];
573
+ }
574
+
575
+ return analyzeOxcExpressionChild(code, expression, context, bodyStatementJsx);
576
+ }
577
+
578
+ function analyzeOxcListExpression(
579
+ code: string,
580
+ expression: Record<string, unknown>,
581
+ context: OxcChildAnalysisContext,
582
+ bodyStatementJsx: OxcBodyStatementJsxMode,
583
+ ): JsxNodeIr | undefined {
584
+ const callExpression =
585
+ expression.type === "ChainExpression" ? readObject(expression.expression) : expression;
586
+
587
+ if (callExpression.type !== "CallExpression") {
588
+ return undefined;
589
+ }
590
+
591
+ const callee = readObject(callExpression.callee);
592
+
593
+ if (callee.type !== "MemberExpression" || readObject(callee.property).name !== "map") {
594
+ return undefined;
595
+ }
596
+
597
+ const renderer = readObject(readArray(callExpression.arguments)[0]);
598
+
599
+ if (renderer.type !== "ArrowFunctionExpression") {
600
+ return undefined;
601
+ }
602
+
603
+ const itemName = String(readObject(readArray(renderer.params)[0]).name ?? "_item");
604
+ const indexName = readObject(readArray(renderer.params)[1]).name;
605
+ const rendererBody = analyzeOxcListRenderer(code, renderer, context, bodyStatementJsx);
606
+
607
+ if (rendererBody === undefined) {
608
+ return undefined;
609
+ }
610
+
611
+ const { children, bodyStatements } = rendererBody;
612
+ const keyCode = findOxcKeyCodeInChildren(children);
613
+
614
+ return {
615
+ kind: "list",
616
+ itemsCode:
617
+ callee.optional === true
618
+ ? `(${readOxcReactiveExpressionCode(code, readObject(callee.object), context)} ?? [])`
619
+ : readOxcReactiveExpressionCode(code, readObject(callee.object), context),
620
+ itemName,
621
+ ...(typeof indexName === "string" ? { indexName } : {}),
622
+ ...(keyCode === undefined ? {} : { keyCode }),
623
+ ...(bodyStatements.length === 0 ? {} : { bodyStatements }),
624
+ children,
625
+ };
626
+ }
627
+
628
+ function analyzeOxcListRenderer(
629
+ code: string,
630
+ renderer: Record<string, unknown>,
631
+ context: OxcChildAnalysisContext,
632
+ bodyStatementJsx: OxcBodyStatementJsxMode,
633
+ ): { children: JsxNodeIr[]; bodyStatements: string[] } | undefined {
634
+ const body = readObject(renderer.body);
635
+
636
+ if (body.type !== "BlockStatement") {
637
+ return analyzeOxcListReturnExpression(
638
+ code,
639
+ unwrapOxcParentheses(body),
640
+ [],
641
+ context,
642
+ bodyStatementJsx,
643
+ );
644
+ }
645
+
646
+ const statements = readArray(body.body);
647
+ const ifStatementIndex = statements.findIndex(
648
+ (statement) => readObject(statement).type === "IfStatement",
649
+ );
650
+
651
+ if (ifStatementIndex >= 0) {
652
+ return analyzeOxcListIfRenderer(code, statements, ifStatementIndex, context, bodyStatementJsx);
653
+ }
654
+
655
+ const returnStatementIndex = statements.findIndex(
656
+ (statement) => readObject(statement).type === "ReturnStatement",
657
+ );
658
+ const returnStatement =
659
+ returnStatementIndex === -1 ? undefined : readObject(statements[returnStatementIndex]);
660
+ const returnArgument =
661
+ returnStatement === undefined
662
+ ? undefined
663
+ : unwrapOxcParentheses(readObject(returnStatement.argument));
664
+
665
+ if (returnArgument === undefined) {
666
+ return undefined;
667
+ }
668
+
669
+ const bodyPrefixStatements = statements.slice(0, returnStatementIndex);
670
+ const result = analyzeOxcListReturnExpression(
671
+ code,
672
+ returnArgument,
673
+ bodyPrefixStatements.map(
674
+ (statement) =>
675
+ lowerOxcBodyStatementJsx(
676
+ code,
677
+ statement,
678
+ context.componentNames,
679
+ context.target,
680
+ context.diagnostics,
681
+ bodyStatementJsx,
682
+ context.bodyLowerers,
683
+ ) ?? formatOxcBodyStatement(code, statement, bodyStatementJsx),
684
+ ),
685
+ context,
686
+ bodyStatementJsx,
687
+ );
688
+
689
+ if (result === undefined) {
690
+ return undefined;
691
+ }
692
+
693
+ markOxcRenderValueExpressions(
694
+ result.children,
695
+ collectOxcBodyJsxBindingNames(bodyPrefixStatements),
696
+ );
697
+ return result;
698
+ }
699
+
700
+ function analyzeOxcListReturnExpression(
701
+ code: string,
702
+ body: Record<string, unknown>,
703
+ bodyStatements: string[],
704
+ context: OxcChildAnalysisContext,
705
+ bodyStatementJsx: OxcBodyStatementJsxMode,
706
+ ): { children: JsxNodeIr[]; bodyStatements: string[] } | undefined {
707
+ if (body.type === "ConditionalExpression") {
708
+ const children: JsxNodeIr[] = [
709
+ {
710
+ kind: "conditional",
711
+ conditionCode: readOxcReactiveExpressionCode(code, readObject(body.test), context),
712
+ whenTrue: analyzeOxcDynamicBranch(
713
+ code,
714
+ readObject(body.consequent),
715
+ context,
716
+ bodyStatementJsx,
717
+ ),
718
+ whenFalse: analyzeOxcDynamicBranch(
719
+ code,
720
+ readObject(body.alternate),
721
+ context,
722
+ bodyStatementJsx,
723
+ ),
724
+ },
725
+ ];
726
+
727
+ return {
728
+ children,
729
+ bodyStatements,
730
+ };
731
+ }
732
+
733
+ if (body.type !== "JSXElement" && body.type !== "JSXFragment") {
734
+ return undefined;
735
+ }
736
+
737
+ return {
738
+ children: [analyzeOxcJsxNode(code, body, context, bodyStatementJsx)],
739
+ bodyStatements,
740
+ };
741
+ }
742
+
743
+ function analyzeOxcListIfRenderer(
744
+ code: string,
745
+ statements: readonly unknown[],
746
+ ifStatementIndex: number,
747
+ context: OxcChildAnalysisContext,
748
+ bodyStatementJsx: OxcBodyStatementJsxMode,
749
+ ): { children: JsxNodeIr[]; bodyStatements: string[] } | undefined {
750
+ const ifStatement = readObject(statements[ifStatementIndex]);
751
+ const whenTrueExpression = readOxcReturnExpressionFromStatement(ifStatement.consequent);
752
+ const alternate = readOxcReturnExpressionFromStatement(ifStatement.alternate);
753
+ const fallthrough = readOxcReturnExpressionFromStatement(statements[ifStatementIndex + 1]);
754
+ const whenFalseExpression = alternate ?? fallthrough;
755
+
756
+ if (whenTrueExpression === undefined || whenFalseExpression === undefined) {
757
+ return undefined;
758
+ }
759
+
760
+ const bodyPrefixStatements = statements.slice(0, ifStatementIndex);
761
+ const children: JsxNodeIr[] = [
762
+ {
763
+ kind: "conditional",
764
+ conditionCode: readOxcReactiveExpressionCode(code, readObject(ifStatement.test), context),
765
+ whenTrue: analyzeOxcDynamicBranch(code, whenTrueExpression, context, bodyStatementJsx),
766
+ whenFalse: analyzeOxcDynamicBranch(code, whenFalseExpression, context, bodyStatementJsx),
767
+ },
768
+ ];
769
+
770
+ markOxcRenderValueExpressions(children, collectOxcBodyJsxBindingNames(bodyPrefixStatements));
771
+
772
+ return {
773
+ bodyStatements: bodyPrefixStatements.map(
774
+ (statement) =>
775
+ lowerOxcBodyStatementJsx(
776
+ code,
777
+ statement,
778
+ context.componentNames,
779
+ context.target,
780
+ context.diagnostics,
781
+ bodyStatementJsx,
782
+ context.bodyLowerers,
783
+ ) ?? formatOxcBodyStatement(code, statement, bodyStatementJsx),
784
+ ),
785
+ children,
786
+ };
787
+ }
788
+
789
+ function resolveOxcBodyStatementJsx(context: OxcChildAnalysisContext): OxcBodyStatementJsxMode {
790
+ return context.bodyStatementJsx ?? (context.target === "server" ? "server-string" : "dom-node");
791
+ }