@jay-framework/compiler-jay-html 0.6.10 → 0.8.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.
@@ -176,3 +176,139 @@ Since Jay uses immutable view state, the `trackBy` attribute is essential for pr
176
176
  ```
177
177
 
178
178
  In this example, each `ProductCard` component receives the entire product object as its context.
179
+
180
+ ## Recursive Rendering
181
+
182
+ Jay-HTML supports recursive component structures where elements can reference themselves, enabling the rendering of tree-like and nested data structures.
183
+
184
+ ### Defining Recursive Types
185
+
186
+ Recursive types are defined in the data contract using the `$/data` or `$/data/path` syntax:
187
+
188
+ ```yaml
189
+ data:
190
+ title: string
191
+ tree:
192
+ id: string
193
+ name: string
194
+ children: $/data/tree
195
+ ```
196
+
197
+ The `$/data/tree` reference creates a recursive type where `children` has the same type as `tree` itself.
198
+
199
+ ### Nested Recursive References
200
+
201
+ You can reference nested types deeper in the data structure:
202
+
203
+ ```yaml
204
+ data:
205
+ root:
206
+ value: number
207
+ nested:
208
+ id: string
209
+ children: $/data/root/nested
210
+ ```
211
+
212
+ This creates a recursive type at `root.nested` where `children` has the same type as the parent `nested` object.
213
+
214
+ ### Creating Recursive Regions
215
+
216
+ Use the `ref` attribute to mark an element as a recursive region, then use `<recurse>` to trigger recursion:
217
+
218
+ ```html
219
+ <div class="tree-node" ref="treeNode">
220
+ <div class="node-name">{name}</div>
221
+ <div if="children">
222
+ <recurse ref="treeNode" accessor="children" />
223
+ </div>
224
+ </div>
225
+ ```
226
+
227
+ The compiler generates a recursive function for the referenced element, and `<recurse>` calls that function with the specified data.
228
+
229
+ ### Context Switching with `<with-data>`
230
+
231
+ When your recursive data is nested within a parent structure, use `<with-data>` to switch the view state context:
232
+
233
+ ```yaml
234
+ data:
235
+ title: string
236
+ description: string
237
+ btree:
238
+ value: number
239
+ left: $/data/btree
240
+ right: $/data/btree
241
+ ```
242
+
243
+ ```html
244
+ <div class="tree-container">
245
+ <h1>{title}</h1>
246
+ <p>{description}</p>
247
+ <with-data accessor="btree">
248
+ <div class="tree-node" ref="treeNode">
249
+ <div>{value}</div>
250
+ <div if="left">
251
+ <recurse ref="treeNode" accessor="left" />
252
+ </div>
253
+ <div if="right">
254
+ <recurse ref="treeNode" accessor="right" />
255
+ </div>
256
+ </div>
257
+ </with-data>
258
+ </div>
259
+ ```
260
+
261
+ The `<with-data>` element:
262
+
263
+ - Accepts an `accessor` attribute specifying a property path
264
+ - Changes the view state context for its children
265
+ - Must have exactly one child element
266
+ - Works with both object and array types
267
+
268
+ This allows the recursive region to operate on a consistent type (`btree`) whether it's at the root level or in a recursive call.
269
+
270
+ ### Identity Accessor with `forEach`
271
+
272
+ Within a `<with-data>` context, you can use `forEach="."` to iterate over the current context:
273
+
274
+ ```html
275
+ <with-data accessor="tree">
276
+ <ul ref="menuItem">
277
+ <li forEach="." trackBy="id">
278
+ <span>{name}</span>
279
+ <div if="children">
280
+ <recurse ref="menuItem" accessor="children" />
281
+ </div>
282
+ </li>
283
+ </ul>
284
+ </with-data>
285
+ ```
286
+
287
+ The `forEach="."` syntax means "iterate over the current context", which is useful when `<with-data>` has already narrowed the context to an array.
288
+
289
+ ### Recursion Guards
290
+
291
+ Recursion requires proper guards to prevent infinite loops:
292
+
293
+ - **Recursion with accessor** (e.g., `<recurse accessor="children"/>`) uses `withData` which includes a built-in null check
294
+ - **Recursion without accessor** (e.g., `<recurse/>` in a forEach loop) must be inside a `forEach` or conditional to prevent infinite recursion
295
+
296
+ Example with forEach (no accessor needed):
297
+
298
+ ```html
299
+ <ul ref="list">
300
+ <li forEach="items" trackBy="id">
301
+ {text}
302
+ <recurse ref="list" />
303
+ </li>
304
+ </ul>
305
+ ```
306
+
307
+ Example with accessor (built-in guard):
308
+
309
+ ```html
310
+ <div ref="node">
311
+ {value}
312
+ <recurse ref="node" accessor="child" />
313
+ </div>
314
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jay-framework/compiler-jay-html",
3
- "version": "0.6.10",
3
+ "version": "0.8.0",
4
4
  "description": "",
5
5
  "license": "Apache-2.0",
6
6
  "main": "dist/index.js",
@@ -34,11 +34,11 @@
34
34
  },
35
35
  "author": "",
36
36
  "dependencies": {
37
- "@jay-framework/compiler-analyze-exported-types": "^0.6.10",
38
- "@jay-framework/compiler-shared": "^0.6.10",
39
- "@jay-framework/component": "^0.6.10",
40
- "@jay-framework/runtime": "^0.6.10",
41
- "@jay-framework/secure": "^0.6.10",
37
+ "@jay-framework/compiler-analyze-exported-types": "^0.8.0",
38
+ "@jay-framework/compiler-shared": "^0.8.0",
39
+ "@jay-framework/component": "^0.8.0",
40
+ "@jay-framework/runtime": "^0.8.0",
41
+ "@jay-framework/secure": "^0.8.0",
42
42
  "@types/js-yaml": "^4.0.9",
43
43
  "change-case": "^4.1.2",
44
44
  "js-yaml": "^4.1.0",
@@ -50,8 +50,8 @@
50
50
  },
51
51
  "devDependencies": {
52
52
  "@caiogondim/strip-margin": "^1.0.0",
53
- "@jay-framework/4-react": "^0.6.10",
54
- "@jay-framework/dev-environment": "^0.6.10",
53
+ "@jay-framework/4-react": "^0.8.0",
54
+ "@jay-framework/dev-environment": "^0.8.0",
55
55
  "@testing-library/jest-dom": "^6.2.0",
56
56
  "@types/js-beautify": "^1",
57
57
  "@types/node": "^20.11.5",
package/readme.md CHANGED
@@ -26,7 +26,7 @@ Here's a simple Jay-HTML file:
26
26
 
27
27
  ## Key Differences from Standard HTML
28
28
 
29
- Jay-HTML extends HTML with seven main features:
29
+ Jay-HTML extends HTML with eight main features:
30
30
 
31
31
  1. **Component and type imports** - Import reusable components and type definitions
32
32
  2. **Data contract definition** - Define component interfaces using YAML
@@ -35,6 +35,7 @@ Jay-HTML extends HTML with seven main features:
35
35
  5. **Data binding** - Bind component data to HTML using `{}` syntax
36
36
  6. **Conditional rendering** - Show/hide elements based on conditions
37
37
  7. **List rendering** - Iterate over arrays with `forEach` and `trackBy`
38
+ 8. **Async rendering** - Handle promises with loading, resolved, and error states
38
39
 
39
40
  ## Component Import System
40
41
 
@@ -104,6 +105,56 @@ Headless components provide only the contract and logic:
104
105
  ></script>
105
106
  ```
106
107
 
108
+ ## Async Rendering
109
+
110
+ Jay-HTML provides built-in support for handling asynchronous data with dedicated conditional rendering attributes. Async data can be primitive types (like strings or numbers), objects, or arrays.
111
+
112
+ ### Async Data Declaration
113
+
114
+ ```html
115
+ <script type="application/yaml-jay">
116
+ data:
117
+ title: string
118
+ async status: string <!-- Primitive type -->
119
+ async userProfile: <!-- Object type -->
120
+ name: string
121
+ email: string
122
+ async posts: <!-- Array type -->
123
+ - id: string
124
+ title: string
125
+ </script>
126
+ ```
127
+
128
+ ### Async State Handling
129
+
130
+ ```html
131
+ <body>
132
+ <h1>{title}</h1>
133
+
134
+ <!-- Loading state -->
135
+ <div when-loading="userProfile">Loading user profile...</div>
136
+
137
+ <!-- Success state -->
138
+ <div when-resolved="userProfile">
139
+ <h2>Welcome, {name}!</h2>
140
+ <p>{email}</p>
141
+ </div>
142
+
143
+ <!-- Error state -->
144
+ <div when-rejected="userProfile">
145
+ Failed to load profile: {message}
146
+ <!-- Error properties available: {name}, {message}, {stack} -->
147
+ </div>
148
+
149
+ <!-- Async arrays -->
150
+ <div when-resolved="posts">
151
+ <article forEach="." trackBy="id">
152
+ <h3>{title}</h3>
153
+ </article>
154
+ </div>
155
+ </body>
156
+ ```
157
+
107
158
  ## Documentation
108
159
 
109
160
  ### Jay-HTML Syntax
@@ -0,0 +1,142 @@
1
+ # Recursive Jay-HTML Test Fixtures
2
+
3
+ These fixtures test the new recursive jay-html feature as designed in Design Log 46.
4
+
5
+ ## Feature Overview
6
+
7
+ The recursive jay-html feature allows marking HTML subtrees as recursive regions using:
8
+
9
+ - `ref="regionName"` on an element to mark it as a recursive region
10
+ - `<recurse ref="regionName" />` to trigger recursion at that point
11
+ - `array<$/data>` type syntax to define recursive type references
12
+
13
+ ## Test Cases
14
+
15
+ ### 1. `simple-tree/`
16
+
17
+ **Tests:** Basic direct recursion with a simple tree structure
18
+
19
+ **Key Features:**
20
+
21
+ - Direct recursion: `children: array<$/data>`
22
+ - Recursive region marked with `ref="treeNode"`
23
+ - Recursion triggered in `forEach` with `<recurse ref="treeNode" />`
24
+ - Has a ref within the recursive region (`nodeHeader`)
25
+ - Conditional rendering with `if="open"`
26
+
27
+ ### 2. `indirect-recursion/`
28
+
29
+ **Tests:** Indirect recursion through a nested container object
30
+
31
+ **Key Features:**
32
+
33
+ - Indirect recursion: `submenu.items: array<$/data>`
34
+ - The recursion doesn't happen directly on the root type
35
+ - Demonstrates type generation for nested containers with recursive arrays
36
+ - Combined conditional: `if="hasSubmenu && isOpen"`
37
+
38
+ ### 3. `tree-with-conditional/`
39
+
40
+ **Tests:** Recursive structure with enum types and complex conditionals
41
+
42
+ **Key Features:**
43
+
44
+ - Enum type usage: `type: enum (file | folder)`
45
+ - Conditional recursion based on enum: `if="type == folder && isExpanded"`
46
+ - Demonstrates enum code generation in recursive contexts
47
+ - Shows how enum comparisons work in recursive regions
48
+
49
+ ### 4. `nested-comments/`
50
+
51
+ **Tests:** Classic nested comments/replies pattern
52
+
53
+ **Key Features:**
54
+
55
+ - Simple direct recursion for comment threads
56
+ - Button ref within recursive region (`toggleReplies`)
57
+ - Demonstrates real-world use case (threaded comments)
58
+ - Clean parent-child relationship pattern
59
+
60
+ ### 5. `linked-list/`
61
+
62
+ **Tests:** Recursive conditional without arrays (single optional child)
63
+
64
+ **Key Features:**
65
+
66
+ - Non-array recursion: `next: $/data` (not an array!)
67
+ - Linked list pattern with optional next node
68
+ - Recursion guard is conditional only (no forEach)
69
+ - Type generation: `next: LinkedListViewState | null`
70
+ - Demonstrates single-child recursive structures
71
+
72
+ ### 6. `binary-tree/`
73
+
74
+ **Tests:** Multiple recursive conditionals (left/right children)
75
+
76
+ **Key Features:**
77
+
78
+ - Multiple non-array recursive references: `left: $/data`, `right: $/data`
79
+ - Binary tree pattern with optional left and right children
80
+ - Two separate conditional recursions in same region
81
+ - Type generation: nullable optional children
82
+ - Demonstrates branching recursive structures
83
+
84
+ ## Expected Generated Code Pattern
85
+
86
+ Each test expects code generation following this pattern:
87
+
88
+ ```typescript
89
+ // 1. Recursive ViewState type with self-reference
90
+ export interface TreeViewState {
91
+ // ... properties
92
+ children: Array<TreeViewState>; // Self-referencing array
93
+ }
94
+
95
+ // 2. Internal recursive render function
96
+ function renderRecursiveRegion_<refName>(data: TreeViewState) {
97
+ return e('div', {}, [
98
+ // ... element structure
99
+ forEach(
100
+ (vs) => vs.children,
101
+ (childData) => {
102
+ return e('li', {}, [
103
+ renderRecursiveRegion_<refName>(childData), // Recursive call
104
+ ]);
105
+ },
106
+ 'trackByKey',
107
+ ),
108
+ ]);
109
+ }
110
+
111
+ // 3. Main render function that calls the recursive function
112
+ const render = (viewState: TreeViewState) =>
113
+ ConstructContext.withRootContext(viewState, refManager, () =>
114
+ e('div', {}, [
115
+ // Non-recursive content
116
+ renderRecursiveRegion_<refName>(viewState), // Initial call
117
+ ]),
118
+ ) as TreeElement;
119
+ ```
120
+
121
+ ## Validation Rules Being Tested
122
+
123
+ 1. **Recursion Guards**: Each `<recurse>` must be inside a `forEach` or conditional
124
+ - forEach guard: Tests 1-4 (array-based recursion)
125
+ - Conditional guard: Tests 5-6 (non-array recursion)
126
+ 2. **Type Consistency**: The array/property type must match the recursive type reference
127
+ - Array recursion: `array<$/data>` → `Array<ViewState>`
128
+ - Single recursion: `$/data` → `ViewState | null`
129
+ 3. **Valid Region References**: `<recurse ref="name">` must reference an existing `ref="name"` element
130
+ 4. **Descendant Requirement**: `<recurse>` must be a descendant of the referenced region
131
+
132
+ ## Not Yet Tested
133
+
134
+ These fixtures do **not** yet test:
135
+
136
+ - Multiple recursive regions in one component
137
+ - Referencing nested types (`array<$/data/metadata>`)
138
+ - Error cases (missing guards, invalid references, etc.)
139
+ - React target generation for recursive structures
140
+ - Contract file recursion with `link: $/`
141
+
142
+ Additional test fixtures should be added for these scenarios.