* Build a component node recursively * In streaming mode, tracks pending components that are not yet defined
(
definition: ComponentDefinition,
surface: Surface,
visited: Set<string>,
streamingState?: StreamingState,
)
| 597 | * In streaming mode, tracks pending components that are not yet defined |
| 598 | */ |
| 599 | private buildComponentNode( |
| 600 | definition: ComponentDefinition, |
| 601 | surface: Surface, |
| 602 | visited: Set<string>, |
| 603 | streamingState?: StreamingState, |
| 604 | ): AnyComponentNode | null { |
| 605 | // Cycle detection |
| 606 | if (visited.has(definition.id)) { |
| 607 | console.warn(`Circular reference detected for component ${definition.id}.`); |
| 608 | return null; |
| 609 | } |
| 610 | visited.add(definition.id); |
| 611 | |
| 612 | const typeName = getComponentTypeName(definition.component); |
| 613 | const props = getComponentProps<Record<string, unknown>>(definition.component); |
| 614 | |
| 615 | // Resolve property values |
| 616 | const resolvedProps = this.resolveProps(props, surface.dataModel); |
| 617 | |
| 618 | // Build children |
| 619 | const children = this.buildChildren(props, surface, visited, streamingState); |
| 620 | |
| 621 | // Track rendered component in streaming state |
| 622 | if (streamingState) { |
| 623 | streamingState.renderedComponentIds.add(definition.id); |
| 624 | } |
| 625 | |
| 626 | // Remove from visited after processing (allow same component in different branches) |
| 627 | visited.delete(definition.id); |
| 628 | |
| 629 | return { |
| 630 | id: definition.id, |
| 631 | type: typeName, |
| 632 | props: resolvedProps, |
| 633 | children: children.length > 0 ? children : undefined, |
| 634 | }; |
| 635 | } |
| 636 | |
| 637 | /** |
| 638 | * Resolve property values from data model |
no test coverage detected