@mdxui/terminal 2.0.0

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 (191) hide show
  1. package/README.md +571 -0
  2. package/dist/ansi-css-Sk5mWtdK.d.ts +119 -0
  3. package/dist/ansi-css-V6JIHGsM.d.ts +119 -0
  4. package/dist/ansi-css-_3eSEU9d.d.ts +119 -0
  5. package/dist/chunk-3EFDH7PK.js +5235 -0
  6. package/dist/chunk-3RG5ZIWI.js +10 -0
  7. package/dist/chunk-3X5IR6WE.js +884 -0
  8. package/dist/chunk-4FV5ZDCE.js +5236 -0
  9. package/dist/chunk-4OVMSF2J.js +243 -0
  10. package/dist/chunk-63FEETIS.js +4048 -0
  11. package/dist/chunk-B43KP7XJ.js +884 -0
  12. package/dist/chunk-BMTJXWUV.js +655 -0
  13. package/dist/chunk-C3SVH4N7.js +882 -0
  14. package/dist/chunk-EVWR7Y47.js +874 -0
  15. package/dist/chunk-F6A5VWUC.js +1285 -0
  16. package/dist/chunk-FD7KW7GE.js +882 -0
  17. package/dist/chunk-GBQ6UD6I.js +655 -0
  18. package/dist/chunk-GMDD3M6U.js +5227 -0
  19. package/dist/chunk-JBHRXOXM.js +1058 -0
  20. package/dist/chunk-JFOO3EYO.js +1182 -0
  21. package/dist/chunk-JQ5H3WXL.js +1291 -0
  22. package/dist/chunk-JQD5NASE.js +234 -0
  23. package/dist/chunk-KRHJP5R7.js +592 -0
  24. package/dist/chunk-KWF6WVJE.js +962 -0
  25. package/dist/chunk-LHYQVN3H.js +1038 -0
  26. package/dist/chunk-M3TLQLGC.js +1032 -0
  27. package/dist/chunk-MVW4Q5OP.js +240 -0
  28. package/dist/chunk-NXCZSWLU.js +1294 -0
  29. package/dist/chunk-O25TNRO6.js +607 -0
  30. package/dist/chunk-PNECDA2I.js +884 -0
  31. package/dist/chunk-QIHWRLJR.js +962 -0
  32. package/dist/chunk-QW5YMQ7K.js +882 -0
  33. package/dist/chunk-R5U7XKVJ.js +16 -0
  34. package/dist/chunk-RP2MVQLR.js +962 -0
  35. package/dist/chunk-TP6RXGXA.js +1087 -0
  36. package/dist/chunk-TQQSTITZ.js +655 -0
  37. package/dist/chunk-X24GWXQV.js +1281 -0
  38. package/dist/components/index.d.ts +802 -0
  39. package/dist/components/index.js +149 -0
  40. package/dist/data/index.d.ts +2554 -0
  41. package/dist/data/index.js +51 -0
  42. package/dist/forms/index.d.ts +1596 -0
  43. package/dist/forms/index.js +464 -0
  44. package/dist/index-CQRFZntR.d.ts +867 -0
  45. package/dist/index.d.ts +579 -0
  46. package/dist/index.js +786 -0
  47. package/dist/interactive-D0JkWosD.d.ts +217 -0
  48. package/dist/keyboard/index.d.ts +2 -0
  49. package/dist/keyboard/index.js +43 -0
  50. package/dist/renderers/index.d.ts +546 -0
  51. package/dist/renderers/index.js +2157 -0
  52. package/dist/storybook/index.d.ts +396 -0
  53. package/dist/storybook/index.js +641 -0
  54. package/dist/theme/index.d.ts +1339 -0
  55. package/dist/theme/index.js +123 -0
  56. package/dist/types-Bxu5PAgA.d.ts +710 -0
  57. package/dist/types-CIlop5Ji.d.ts +701 -0
  58. package/dist/types-Ca8p_p5X.d.ts +710 -0
  59. package/package.json +90 -0
  60. package/src/__tests__/components/data/card.test.ts +458 -0
  61. package/src/__tests__/components/data/list.test.ts +473 -0
  62. package/src/__tests__/components/data/metrics.test.ts +541 -0
  63. package/src/__tests__/components/data/table.test.ts +448 -0
  64. package/src/__tests__/components/input/field.test.ts +555 -0
  65. package/src/__tests__/components/input/form.test.ts +870 -0
  66. package/src/__tests__/components/input/search.test.ts +1238 -0
  67. package/src/__tests__/components/input/select.test.ts +658 -0
  68. package/src/__tests__/components/navigation/breadcrumb.test.ts +923 -0
  69. package/src/__tests__/components/navigation/command-palette.test.ts +1095 -0
  70. package/src/__tests__/components/navigation/sidebar.test.ts +1018 -0
  71. package/src/__tests__/components/navigation/tabs.test.ts +995 -0
  72. package/src/__tests__/components.test.tsx +1197 -0
  73. package/src/__tests__/core/compiler.test.ts +986 -0
  74. package/src/__tests__/core/parser.test.ts +785 -0
  75. package/src/__tests__/core/tier-switcher.test.ts +1103 -0
  76. package/src/__tests__/core/types.test.ts +1398 -0
  77. package/src/__tests__/data/collections.test.ts +1337 -0
  78. package/src/__tests__/data/db.test.ts +1265 -0
  79. package/src/__tests__/data/reactive.test.ts +1010 -0
  80. package/src/__tests__/data/sync.test.ts +1614 -0
  81. package/src/__tests__/errors.test.ts +660 -0
  82. package/src/__tests__/forms/integration.test.ts +444 -0
  83. package/src/__tests__/integration.test.ts +905 -0
  84. package/src/__tests__/keyboard.test.ts +1791 -0
  85. package/src/__tests__/renderer.test.ts +489 -0
  86. package/src/__tests__/renderers/ansi-css.test.ts +948 -0
  87. package/src/__tests__/renderers/ansi.test.ts +1366 -0
  88. package/src/__tests__/renderers/ascii.test.ts +1360 -0
  89. package/src/__tests__/renderers/interactive.test.ts +2353 -0
  90. package/src/__tests__/renderers/markdown.test.ts +1483 -0
  91. package/src/__tests__/renderers/text.test.ts +1369 -0
  92. package/src/__tests__/renderers/unicode.test.ts +1307 -0
  93. package/src/__tests__/theme.test.ts +639 -0
  94. package/src/__tests__/utils/assertions.ts +685 -0
  95. package/src/__tests__/utils/index.ts +115 -0
  96. package/src/__tests__/utils/test-renderer.ts +381 -0
  97. package/src/__tests__/utils/utils.test.ts +560 -0
  98. package/src/components/containers/card.ts +56 -0
  99. package/src/components/containers/dialog.ts +53 -0
  100. package/src/components/containers/index.ts +9 -0
  101. package/src/components/containers/panel.ts +59 -0
  102. package/src/components/feedback/badge.ts +40 -0
  103. package/src/components/feedback/index.ts +8 -0
  104. package/src/components/feedback/spinner.ts +23 -0
  105. package/src/components/helpers.ts +81 -0
  106. package/src/components/index.ts +153 -0
  107. package/src/components/layout/breadcrumb.ts +31 -0
  108. package/src/components/layout/index.ts +10 -0
  109. package/src/components/layout/list.ts +29 -0
  110. package/src/components/layout/sidebar.ts +79 -0
  111. package/src/components/layout/table.ts +62 -0
  112. package/src/components/primitives/box.ts +95 -0
  113. package/src/components/primitives/button.ts +54 -0
  114. package/src/components/primitives/index.ts +11 -0
  115. package/src/components/primitives/input.ts +88 -0
  116. package/src/components/primitives/select.ts +97 -0
  117. package/src/components/primitives/text.ts +60 -0
  118. package/src/components/render.ts +155 -0
  119. package/src/components/templates/app.ts +43 -0
  120. package/src/components/templates/index.ts +8 -0
  121. package/src/components/templates/site.ts +54 -0
  122. package/src/components/types.ts +777 -0
  123. package/src/core/compiler.ts +718 -0
  124. package/src/core/parser.ts +127 -0
  125. package/src/core/tier-switcher.ts +607 -0
  126. package/src/core/types.ts +672 -0
  127. package/src/data/collection.ts +316 -0
  128. package/src/data/collections.ts +50 -0
  129. package/src/data/context.tsx +174 -0
  130. package/src/data/db.ts +127 -0
  131. package/src/data/hooks.ts +532 -0
  132. package/src/data/index.ts +138 -0
  133. package/src/data/reactive.ts +1225 -0
  134. package/src/data/saas-collections.ts +375 -0
  135. package/src/data/sync.ts +1213 -0
  136. package/src/data/types.ts +660 -0
  137. package/src/forms/converters.ts +512 -0
  138. package/src/forms/index.ts +133 -0
  139. package/src/forms/schemas.ts +403 -0
  140. package/src/forms/types.ts +476 -0
  141. package/src/index.ts +542 -0
  142. package/src/keyboard/focus.ts +748 -0
  143. package/src/keyboard/index.ts +96 -0
  144. package/src/keyboard/integration.ts +371 -0
  145. package/src/keyboard/manager.ts +377 -0
  146. package/src/keyboard/presets.ts +90 -0
  147. package/src/renderers/ansi-css.ts +576 -0
  148. package/src/renderers/ansi.ts +802 -0
  149. package/src/renderers/ascii.ts +680 -0
  150. package/src/renderers/breadcrumb.ts +480 -0
  151. package/src/renderers/command-palette.ts +802 -0
  152. package/src/renderers/components/field.ts +210 -0
  153. package/src/renderers/components/form.ts +327 -0
  154. package/src/renderers/components/index.ts +21 -0
  155. package/src/renderers/components/search.ts +449 -0
  156. package/src/renderers/components/select.ts +222 -0
  157. package/src/renderers/index.ts +101 -0
  158. package/src/renderers/interactive/component-handlers.ts +622 -0
  159. package/src/renderers/interactive/cursor-manager.ts +147 -0
  160. package/src/renderers/interactive/focus-manager.ts +279 -0
  161. package/src/renderers/interactive/index.ts +661 -0
  162. package/src/renderers/interactive/input-handler.ts +164 -0
  163. package/src/renderers/interactive/keyboard-handler.ts +212 -0
  164. package/src/renderers/interactive/mouse-handler.ts +167 -0
  165. package/src/renderers/interactive/state-manager.ts +109 -0
  166. package/src/renderers/interactive/types.ts +338 -0
  167. package/src/renderers/interactive-string.ts +299 -0
  168. package/src/renderers/interactive.ts +59 -0
  169. package/src/renderers/markdown.ts +950 -0
  170. package/src/renderers/sidebar.ts +549 -0
  171. package/src/renderers/tabs.ts +682 -0
  172. package/src/renderers/text.ts +791 -0
  173. package/src/renderers/unicode.ts +917 -0
  174. package/src/renderers/utils.ts +942 -0
  175. package/src/router/adapters.ts +383 -0
  176. package/src/router/types.ts +140 -0
  177. package/src/router/utils.ts +452 -0
  178. package/src/schemas.ts +205 -0
  179. package/src/storybook/index.ts +91 -0
  180. package/src/storybook/interactive-decorator.tsx +659 -0
  181. package/src/storybook/keyboard-simulator.ts +501 -0
  182. package/src/theme/ansi-codes.ts +80 -0
  183. package/src/theme/box-drawing.ts +132 -0
  184. package/src/theme/color-convert.ts +254 -0
  185. package/src/theme/color-support.ts +321 -0
  186. package/src/theme/index.ts +134 -0
  187. package/src/theme/strip-ansi.ts +50 -0
  188. package/src/theme/tailwind-map.ts +469 -0
  189. package/src/theme/text-styles.ts +206 -0
  190. package/src/theme/theme-system.ts +568 -0
  191. package/src/types.ts +103 -0
@@ -0,0 +1,718 @@
1
+ /**
2
+ * @mdxui/terminal JSX to UINode Compiler
3
+ *
4
+ * Compiles React element trees into UINode trees for terminal rendering.
5
+ *
6
+ * @remarks
7
+ * This module provides a TDD-implemented JSX compiler that converts React's
8
+ * dynamic component tree model into a serializable UINode tree optimized for
9
+ * terminal rendering. The compiler handles all React element types including
10
+ * fragments, nested trees, and edge cases like null/undefined filtering.
11
+ *
12
+ * **Performance Characteristics:**
13
+ * - Single-pass traversal: O(n) where n = total nodes
14
+ * - No backtracking or re-traversal of elements
15
+ * - Memory-efficient: Reuses object literals where safe
16
+ * - Deep tree optimization: Tail-call patterns for stack efficiency
17
+ * - Allocation minimization: Early returns avoid intermediate arrays
18
+ *
19
+ * **Key Design Principles:**
20
+ * 1. **Type Safety**: All child types validated at process time
21
+ * 2. **Fragment Flattening**: React Fragments unwrapped during traversal
22
+ * 3. **Null Filtering**: React's null/undefined/boolean children filtered out
23
+ * 4. **Key Preservation**: React keys converted to string keys for reconciliation
24
+ * 5. **Props Isolation**: Children field excluded from props object
25
+ *
26
+ * **Usage Example:**
27
+ * ```typescript
28
+ * import { compileJSX } from '@mdxui/terminal'
29
+ *
30
+ * const jsx = <Box padding={2}>
31
+ * <Text bold>Hello</Text>
32
+ * <Text>World</Text>
33
+ * </Box>
34
+ *
35
+ * const uiTree = compileJSX(jsx)
36
+ * // Result:
37
+ * // {
38
+ * // type: 'Box',
39
+ * // props: { padding: 2 },
40
+ * // children: [
41
+ * // { type: 'Text', props: { bold: true }, children: [{ type: 'Text', props: {}, children: ['Hello'] }] },
42
+ * // { type: 'Text', props: {}, children: ['World'] }
43
+ * // ]
44
+ * // }
45
+ * ```
46
+ *
47
+ * @category Compiler
48
+ * @module compiler
49
+ */
50
+ import React from 'react'
51
+ import type { UINode } from './types'
52
+
53
+ // ============================================================================
54
+ // Type Definitions
55
+ // ============================================================================
56
+
57
+ /**
58
+ * Union type for child elements in a UINode's children array.
59
+ *
60
+ * @remarks
61
+ * Children can be either structured UINode objects or text strings.
62
+ * Numbers are converted to strings during processing.
63
+ * Null, undefined, and boolean children are filtered out to match React semantics.
64
+ *
65
+ * @see {@link UINode}
66
+ */
67
+ type UIChild = UINode | string
68
+
69
+ // ============================================================================
70
+ // Constants
71
+ // ============================================================================
72
+
73
+ /**
74
+ * Fallback type name for anonymous components without displayName or function name.
75
+ *
76
+ * @remarks
77
+ * Used when a component is created from an anonymous function or class,
78
+ * and no explicit displayName has been set. This ensures every UINode
79
+ * has a valid string type, even for edge-case components.
80
+ */
81
+ const UNKNOWN_COMPONENT_TYPE = 'Unknown'
82
+
83
+ // ============================================================================
84
+ // Helper Functions
85
+ // ============================================================================
86
+
87
+ /**
88
+ * Extracts the string type name from a React element type.
89
+ *
90
+ * @param type - The type property from a React element (string, function, or class)
91
+ * @returns String representation of the component type
92
+ *
93
+ * @remarks
94
+ * - String types (intrinsic elements like 'div') are returned as-is
95
+ * - Function/class components return their `.name` property
96
+ * - Anonymous components fall back to {@link UNKNOWN_COMPONENT_TYPE}
97
+ *
98
+ * @example
99
+ * ```typescript
100
+ * resolveTypeName('div') // 'div'
101
+ * resolveTypeName(MyComponent) // 'MyComponent'
102
+ * resolveTypeName(() => null) // 'Unknown'
103
+ * ```
104
+ *
105
+ * @internal
106
+ */
107
+ function resolveTypeName(type: React.ReactElement['type']): string {
108
+ if (typeof type === 'string') {
109
+ return type
110
+ }
111
+ return (type as React.FC).name || UNKNOWN_COMPONENT_TYPE
112
+ }
113
+
114
+ /**
115
+ * Checks if a value should be filtered out from children.
116
+ *
117
+ * @param value - Any child value to check
118
+ * @returns true if the value should be excluded from children array
119
+ *
120
+ * @remarks
121
+ * React filters out null, undefined, and boolean values from rendered output.
122
+ * This matches that behavior for UINode children arrays.
123
+ *
124
+ * @internal
125
+ */
126
+ function isFilterableChild(value: unknown): value is null | undefined | boolean {
127
+ return value === null || value === undefined || typeof value === 'boolean'
128
+ }
129
+
130
+ // ============================================================================
131
+ // Main Compiler Function
132
+ // ============================================================================
133
+
134
+ /**
135
+ * Compiles a React JSX element into a UINode tree for terminal rendering.
136
+ *
137
+ * Converts React's dynamic element tree into a serializable form suitable for
138
+ * cross-tier terminal rendering (text, markdown, ascii, unicode, ansi, interactive).
139
+ *
140
+ * @param element - A React element created via React.createElement or JSX
141
+ * @returns A UINode tree representing the compiled element with all descendants
142
+ *
143
+ * @remarks
144
+ * **Input Handling:**
145
+ * - Accepts React elements from `React.createElement()` or JSX syntax
146
+ * - Component types (functions, classes, strings) are normalized to strings
147
+ * - Props are extracted and children are processed separately
148
+ * - React keys are preserved for list reconciliation
149
+ *
150
+ * **Output Structure:**
151
+ * - Always returns an object with `type` (string) and `props` (object)
152
+ * - Includes `children` array only if children exist or were explicitly provided
153
+ * - Includes `key` field only if key was present in source element
154
+ * - All props except `children` are preserved as-is
155
+ *
156
+ * **Fragment Handling:**
157
+ * Fragments are automatically unwrapped:
158
+ * ```typescript
159
+ * // Input
160
+ * <Box>
161
+ * <>
162
+ * <Text>A</Text>
163
+ * <Text>B</Text>
164
+ * </>
165
+ * </Box>
166
+ *
167
+ * // Output - Fragment is transparent
168
+ * {
169
+ * type: 'Box',
170
+ * props: {},
171
+ * children: [
172
+ * { type: 'Text', props: {}, children: ['A'] },
173
+ * { type: 'Text', props: {}, children: ['B'] }
174
+ * ]
175
+ * }
176
+ * ```
177
+ *
178
+ * **Key Preservation:**
179
+ * React keys are converted to strings and preserved for reconciliation:
180
+ * ```typescript
181
+ * // Input
182
+ * <List>
183
+ * {items.map((item, i) => <Item key={item.id}>{item.name}</Item>)}
184
+ * </List>
185
+ *
186
+ * // Output - Keys preserved for stable reconciliation
187
+ * {
188
+ * type: 'List',
189
+ * props: {},
190
+ * children: [
191
+ * { type: 'Item', props: {}, key: 'item-1', children: ['Alice'] },
192
+ * { type: 'Item', props: {}, key: 'item-2', children: ['Bob'] }
193
+ * ]
194
+ * }
195
+ * ```
196
+ *
197
+ * **Null/Undefined Filtering:**
198
+ * React's falsy child filtering is applied:
199
+ * ```typescript
200
+ * // Input - null, undefined, and boolean children filtered
201
+ * <Box>
202
+ * {text}
203
+ * {condition && <Element />}
204
+ * {false}
205
+ * {null}
206
+ * {undefined}
207
+ * </Box>
208
+ *
209
+ * // Output - Only truthy content remains
210
+ * {
211
+ * type: 'Box',
212
+ * props: {},
213
+ * children: [
214
+ * { type: 'string', props: {}, children: ['some text'] },
215
+ * { type: 'Element', props: {} } // Only if condition was true
216
+ * ]
217
+ * }
218
+ * ```
219
+ *
220
+ * **Performance Notes:**
221
+ * - Single-pass tree traversal with O(n) complexity
222
+ * - Tail-call optimized for deep component trees
223
+ * - Minimal intermediate allocations
224
+ * - Type checks ordered by frequency (strings before rare types)
225
+ *
226
+ * @example
227
+ * Simple component:
228
+ * ```typescript
229
+ * const element = <Box padding={2} />
230
+ * const result = compileJSX(element)
231
+ * // { type: 'Box', props: { padding: 2 } }
232
+ * ```
233
+ *
234
+ * @example
235
+ * With children:
236
+ * ```typescript
237
+ * const element = <Text bold>Hello World</Text>
238
+ * const result = compileJSX(element)
239
+ * // { type: 'Text', props: { bold: true }, children: ['Hello World'] }
240
+ * ```
241
+ *
242
+ * @example
243
+ * With nested elements and fragment:
244
+ * ```typescript
245
+ * const element = (
246
+ * <Layout>
247
+ * <Header title="App" />
248
+ * <>
249
+ * <Content />
250
+ * <Sidebar />
251
+ * </>
252
+ * <Footer />
253
+ * </Layout>
254
+ * )
255
+ * const result = compileJSX(element)
256
+ * // { type: 'Layout', props: {}, children: [
257
+ * // { type: 'Header', props: { title: 'App' } },
258
+ * // { type: 'Content', props: {} },
259
+ * // { type: 'Sidebar', props: {} },
260
+ * // { type: 'Footer', props: {} }
261
+ * // ]}
262
+ * ```
263
+ *
264
+ * @see {@link UINode} for the output structure
265
+ * @see {@link processChildren} for child processing logic
266
+ * @see {@link processChild} for single child handling
267
+ *
268
+ * @throws {Error} Will not throw - gracefully handles all React element types
269
+ */
270
+ export function compileJSX(element: React.ReactElement): UINode {
271
+ const { type, props, key } = element
272
+
273
+ // Resolve type name using helper (handles strings, functions, classes)
274
+ const typeName = resolveTypeName(type)
275
+
276
+ // Extract props, excluding children (children handled separately)
277
+ const { children: rawChildren, ...restProps } = props || {}
278
+
279
+ // Track whether children were explicitly provided (even if filtered to empty)
280
+ // React distinguishes undefined children from empty children array
281
+ const hasChildrenArg = rawChildren !== undefined
282
+
283
+ // Process children through the recursive children handler
284
+ const processedChildren = processChildren(rawChildren)
285
+
286
+ // Build the result UINode with required fields
287
+ const result: UINode = {
288
+ type: typeName,
289
+ props: restProps || {},
290
+ }
291
+
292
+ // Include children array if:
293
+ // 1. There are processed children to include, OR
294
+ // 2. Children prop was explicitly provided (preserves empty array semantics)
295
+ if (processedChildren.length > 0 || hasChildrenArg) {
296
+ result.children = processedChildren as UINode[]
297
+ }
298
+
299
+ // Preserve React key for list reconciliation (convert to string)
300
+ if (key !== null && key !== undefined) {
301
+ result.key = String(key)
302
+ }
303
+
304
+ return result
305
+ }
306
+
307
+ // ============================================================================
308
+ // Children Processing Functions
309
+ // ============================================================================
310
+
311
+ /**
312
+ * Processes React children into an array of UINode children.
313
+ *
314
+ * Handles all React child types:
315
+ * - Strings and numbers (converted to strings)
316
+ * - React elements (compiled recursively)
317
+ * - React Fragments (unwrapped)
318
+ * - Arrays of any of the above
319
+ * - Filters nullish and boolean values per React semantics
320
+ *
321
+ * @param children - The raw children prop from React (can be any type)
322
+ * @returns Array of UIChild objects (UINode or string), may be empty
323
+ *
324
+ * @remarks
325
+ * **Performance Optimization:**
326
+ * This function uses early returns and conditional branching to avoid unnecessary
327
+ * array allocations:
328
+ * - Null/undefined returns empty array immediately
329
+ * - Array children are flatMapped directly
330
+ * - Single children call processChild without wrapping
331
+ * - No intermediate arrays created except final result
332
+ *
333
+ * **Array Handling:**
334
+ * When children is an array, we use `flatMap` to:
335
+ * - Process each child element
336
+ * - Flatten fragments automatically
337
+ * - Collect all results in a single pass
338
+ *
339
+ * This is more efficient than mapping then filtering, as we only traverse once.
340
+ *
341
+ * **Fragment Unwrapping:**
342
+ * Fragments are transparent to the parent - their children are extracted and
343
+ * flattened into the parent's children array. Nested fragments are recursively
344
+ * unwrapped until only real elements remain.
345
+ *
346
+ * @example
347
+ * ```typescript
348
+ * // String children
349
+ * processChildren('Hello') // ['Hello']
350
+ *
351
+ * // Array of mixed children
352
+ * processChildren(['Text', <Element />, 42])
353
+ * // ['Text', UINode, '42']
354
+ *
355
+ * // Fragment children
356
+ * processChildren(<>A<B /></>)
357
+ * // [UINode, UINode] - Fragment transparent
358
+ *
359
+ * // Null/undefined filtered
360
+ * processChildren([null, 'Text', undefined])
361
+ * // ['Text']
362
+ * ```
363
+ *
364
+ * @internal
365
+ * @see {@link processChild} for single child processing logic
366
+ */
367
+ function processChildren(children: unknown): UIChild[] {
368
+ // Early exit for null/undefined
369
+ // This is the most common case for components without children
370
+ if (children === null || children === undefined) {
371
+ return []
372
+ }
373
+
374
+ // Handle array of children
375
+ // Use flatMap to process and flatten in single pass
376
+ // This handles both direct arrays and flattened fragments
377
+ if (Array.isArray(children)) {
378
+ return children.flatMap((child) => processChild(child))
379
+ }
380
+
381
+ // Handle single child (non-array)
382
+ // Wrap in array via processChild which returns array
383
+ return processChild(children)
384
+ }
385
+
386
+ /**
387
+ * Processes a single child value into an array of UIChild objects.
388
+ *
389
+ * Handles:
390
+ * - Null, undefined, and boolean values: filtered out (returns [])
391
+ * - Strings: passed through as-is
392
+ * - Numbers: converted to strings
393
+ * - React elements: compiled to UINode via recursive compileJSX call
394
+ * - React Fragments: unwrapped to extract children
395
+ * - Arrays: recursively processed with flatMap
396
+ *
397
+ * @param child - A single child value from React
398
+ * @returns Array containing 0 or 1 UIChild, or flattened children if array/fragment
399
+ *
400
+ * @remarks
401
+ * **Type Ordering for Performance:**
402
+ * Checks are ordered by expected frequency:
403
+ * 1. Null/undefined/boolean - very common (conditional rendering)
404
+ * 2. Strings - common (text content)
405
+ * 3. Numbers - less common (numeric text)
406
+ * 4. React elements - less common (nested components)
407
+ * 5. Arrays - rare (should be handled by processChildren)
408
+ *
409
+ * This ordering minimizes the average number of type checks.
410
+ *
411
+ * **Fragment Handling:**
412
+ * When a Fragment is encountered:
413
+ * 1. Extract its children prop
414
+ * 2. Recursively call processChildren to handle unwrapping
415
+ * 3. Return the flattened children array
416
+ * 4. This handles nested fragments automatically
417
+ *
418
+ * **Recursive Compilation:**
419
+ * Regular React elements are compiled by recursively calling compileJSX:
420
+ * - Each element becomes a UINode
421
+ * - Its children are recursively processed
422
+ * - This builds the complete tree structure
423
+ * - Stack depth matches element nesting depth
424
+ *
425
+ * **Array Fallback:**
426
+ * Arrays that reach this function (should be rare) are recursively processed:
427
+ * - This handles edge cases of accidentally nested arrays
428
+ * - Uses flatMap to flatten multiple levels
429
+ * - Ensures complete flattening before returning
430
+ *
431
+ * **Empty Returns:**
432
+ * Many cases return empty array:
433
+ * - Null, undefined, boolean children filtered per React semantics
434
+ * - These don't contribute to the output tree
435
+ * - This is correct and matches React's behavior
436
+ *
437
+ * @example
438
+ * ```typescript
439
+ * processChild(null) // []
440
+ * processChild('text') // ['text']
441
+ * processChild(42) // ['42']
442
+ * processChild(<Element />) // [UINode]
443
+ * processChild(<><A /><B /></>) // [UINode, UINode]
444
+ * processChild(true) // []
445
+ * processChild(false) // []
446
+ * processChild([...]) // [UIChild, ...] flattened
447
+ * ```
448
+ *
449
+ * @internal
450
+ * @see {@link compileJSX} for element compilation
451
+ * @see {@link processChildren} for array child handling
452
+ */
453
+ function processChild(child: unknown): UIChild[] {
454
+ // Filter out null, undefined, and booleans (React's conditional rendering pattern)
455
+ if (isFilterableChild(child)) {
456
+ return []
457
+ }
458
+
459
+ // Strings pass through as-is (most common case after filtering)
460
+ if (typeof child === 'string') {
461
+ return [child]
462
+ }
463
+
464
+ // Numbers are stringified for terminal output
465
+ if (typeof child === 'number') {
466
+ return [String(child)]
467
+ }
468
+
469
+ // React elements (components, intrinsic elements, fragments)
470
+ if (React.isValidElement(child)) {
471
+ const element = child as React.ReactElement
472
+
473
+ // Fragments are transparent - unwrap and process children
474
+ if (element.type === React.Fragment) {
475
+ return processChildren(element.props?.children)
476
+ }
477
+
478
+ // Regular elements compile recursively to UINode
479
+ return [compileJSX(element)]
480
+ }
481
+
482
+ // Arrays (edge case fallback - usually caught by processChildren)
483
+ if (Array.isArray(child)) {
484
+ return child.flatMap((c) => processChild(c))
485
+ }
486
+
487
+ // Unknown types filtered out (matches React behavior)
488
+ return []
489
+ }
490
+
491
+ // ============================================================================
492
+ // Iterative Deep Tree Compiler (Stack Overflow Prevention)
493
+ // ============================================================================
494
+
495
+ /**
496
+ * Work item for iterative tree compilation.
497
+ *
498
+ * @remarks
499
+ * Used by {@link compileJSXDeep} to track pending work during iterative
500
+ * tree traversal. This enables processing arbitrarily deep trees without
501
+ * risk of JavaScript stack overflow.
502
+ *
503
+ * @internal
504
+ */
505
+ interface CompileWorkItem {
506
+ /** The React element to compile */
507
+ element: React.ReactElement
508
+ /** Reference to the parent's children array where result should be added */
509
+ targetArray: UIChild[]
510
+ /** Index in targetArray where this result should be placed */
511
+ targetIndex: number
512
+ }
513
+
514
+ /**
515
+ * Compiles a React JSX element into a UINode tree using iteration.
516
+ *
517
+ * This is an alternative to {@link compileJSX} optimized for deeply nested
518
+ * trees that might cause stack overflow with recursive compilation. Use this
519
+ * when you expect trees with nesting depth > 1000 levels.
520
+ *
521
+ * @param element - A React element created via React.createElement or JSX
522
+ * @returns A UINode tree representing the compiled element with all descendants
523
+ *
524
+ * @remarks
525
+ * **Algorithm:**
526
+ * Uses an explicit work stack to process elements iteratively:
527
+ * 1. Start with root element on the stack
528
+ * 2. Pop element, create UINode with props
529
+ * 3. Process children:
530
+ * - Primitives (string/number): add directly to children array
531
+ * - Fragments: extract children, add to pending work
532
+ * - Elements: add placeholder, push to work stack
533
+ * 4. Continue until stack is empty
534
+ *
535
+ * **Performance:**
536
+ * - O(n) time complexity where n = total nodes
537
+ * - O(d) space complexity where d = maximum tree depth
538
+ * - No recursion limit - can handle 10,000+ nesting levels
539
+ * - Slightly more overhead than recursive version for shallow trees
540
+ *
541
+ * **When to Use:**
542
+ * - Trees with > 1000 nesting levels
543
+ * - Dynamic tree generation where depth is unbounded
544
+ * - Processing user-generated content with arbitrary nesting
545
+ *
546
+ * For most use cases with reasonable tree depths (< 500 levels),
547
+ * use the standard {@link compileJSX} function instead.
548
+ *
549
+ * @example
550
+ * Deep tree compilation:
551
+ * ```typescript
552
+ * // Build a very deep tree
553
+ * let deepElement = <Text>Leaf</Text>
554
+ * for (let i = 0; i < 5000; i++) {
555
+ * deepElement = <Box>{deepElement}</Box>
556
+ * }
557
+ *
558
+ * // This would cause stack overflow with compileJSX
559
+ * // But works fine with compileJSXDeep
560
+ * const uiTree = compileJSXDeep(deepElement)
561
+ * ```
562
+ *
563
+ * @see {@link compileJSX} for the standard recursive implementation
564
+ */
565
+ export function compileJSXDeep(element: React.ReactElement): UINode {
566
+ // Create the root UINode
567
+ const root = createUINodeShell(element)
568
+
569
+ // If no children, we're done
570
+ const rawChildren = element.props?.children
571
+ if (rawChildren === undefined) {
572
+ return root
573
+ }
574
+
575
+ // Initialize children array
576
+ root.children = []
577
+
578
+ // Work stack for iterative processing
579
+ // Each item is [element, targetChildrenArray, targetIndex]
580
+ const workStack: CompileWorkItem[] = []
581
+
582
+ // Process root's children to populate initial work
583
+ populateChildrenWork(rawChildren, root.children, workStack)
584
+
585
+ // Process work stack iteratively
586
+ while (workStack.length > 0) {
587
+ const work = workStack.pop()!
588
+ const node = createUINodeShell(work.element)
589
+
590
+ // Place the compiled node in its target location
591
+ work.targetArray[work.targetIndex] = node
592
+
593
+ // Process this node's children
594
+ const nodeChildren = work.element.props?.children
595
+ if (nodeChildren !== undefined) {
596
+ node.children = []
597
+ populateChildrenWork(nodeChildren, node.children, workStack)
598
+ }
599
+ }
600
+
601
+ return root
602
+ }
603
+
604
+ /**
605
+ * Creates a UINode shell with type, props, and optionally key.
606
+ *
607
+ * Does not process children - that's handled separately for iterative processing.
608
+ *
609
+ * @param element - React element to extract type/props/key from
610
+ * @returns UINode with type and props set, children undefined
611
+ *
612
+ * @internal
613
+ */
614
+ function createUINodeShell(element: React.ReactElement): UINode {
615
+ const { type, props, key } = element
616
+
617
+ // Reuse the same type resolution logic as compileJSX
618
+ const typeName = resolveTypeName(type)
619
+
620
+ // Separate children from other props (children processed separately)
621
+ const { children: _rawChildren, ...restProps } = props || {}
622
+
623
+ const result: UINode = {
624
+ type: typeName,
625
+ props: restProps || {},
626
+ }
627
+
628
+ // Preserve key for reconciliation
629
+ if (key !== null && key !== undefined) {
630
+ result.key = String(key)
631
+ }
632
+
633
+ return result
634
+ }
635
+
636
+ /**
637
+ * Processes children and populates work items for iterative compilation.
638
+ *
639
+ * Adds primitive children directly to the target array and queues
640
+ * React elements for later processing via the work stack.
641
+ *
642
+ * @param children - Raw children value from React element props
643
+ * @param targetArray - Array to add processed children to
644
+ * @param workStack - Stack to push element work items onto
645
+ *
646
+ * @internal
647
+ */
648
+ function populateChildrenWork(
649
+ children: unknown,
650
+ targetArray: UIChild[],
651
+ workStack: CompileWorkItem[]
652
+ ): void {
653
+ // Early exit for null/undefined children
654
+ if (children === null || children === undefined) {
655
+ return
656
+ }
657
+
658
+ // Normalize to array for uniform processing
659
+ const childList: unknown[] = Array.isArray(children) ? children : [children]
660
+
661
+ // Track insertion index for element placeholder placement
662
+ let insertIndex = targetArray.length
663
+
664
+ for (const child of childList) {
665
+ // Skip filterable values (null, undefined, booleans)
666
+ if (isFilterableChild(child)) {
667
+ continue
668
+ }
669
+
670
+ // Strings added directly
671
+ if (typeof child === 'string') {
672
+ targetArray.push(child)
673
+ insertIndex++
674
+ continue
675
+ }
676
+
677
+ // Numbers converted to string
678
+ if (typeof child === 'number') {
679
+ targetArray.push(String(child))
680
+ insertIndex++
681
+ continue
682
+ }
683
+
684
+ // React elements
685
+ if (React.isValidElement(child)) {
686
+ const elem = child as React.ReactElement
687
+
688
+ // Fragments unwrapped inline (not added to work stack)
689
+ if (elem.type === React.Fragment) {
690
+ const fragChildren = elem.props?.children
691
+ if (fragChildren !== null && fragChildren !== undefined) {
692
+ populateChildrenWork(fragChildren, targetArray, workStack)
693
+ insertIndex = targetArray.length
694
+ }
695
+ continue
696
+ }
697
+
698
+ // Regular elements: add placeholder, queue work for later processing
699
+ targetArray.push({} as UINode)
700
+ workStack.push({
701
+ element: elem,
702
+ targetArray,
703
+ targetIndex: insertIndex,
704
+ })
705
+ insertIndex++
706
+ continue
707
+ }
708
+
709
+ // Nested arrays processed recursively
710
+ if (Array.isArray(child)) {
711
+ populateChildrenWork(child, targetArray, workStack)
712
+ insertIndex = targetArray.length
713
+ continue
714
+ }
715
+
716
+ // Unknown types silently ignored (matches React behavior)
717
+ }
718
+ }