@jay-framework/compiler-jay-html 0.8.0 → 0.9.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.
@@ -178,6 +178,220 @@ In this example:
178
178
  4. **Document dependencies** - Make it clear which contracts are required
179
179
  5. **Version contracts** - Consider versioning for breaking changes
180
180
 
181
+ ## Recursive Links
182
+
183
+ Recursive links allow you to create self-referential data structures within a contract, such as trees, nested menus, or hierarchical data. They use the special `$/` syntax to reference parts of the same contract.
184
+
185
+ ### Basic Recursive Link
186
+
187
+ ```yaml
188
+ name: folder-tree
189
+ tags:
190
+ - tag: name
191
+ type: data
192
+ dataType: string
193
+ - tag: children
194
+ type: sub-contract
195
+ repeated: true
196
+ link: $/
197
+ ```
198
+
199
+ The `link: $/` references the root of the current contract, creating a recursive structure.
200
+
201
+ **Generated TypeScript:**
202
+
203
+ ```typescript
204
+ export interface FolderTreeViewState {
205
+ name: string;
206
+ children: Array<FolderTreeViewState>;
207
+ }
208
+ ```
209
+
210
+ ### Nested Path Recursive Links
211
+
212
+ You can reference nested properties within the contract using path syntax:
213
+
214
+ ```yaml
215
+ name: document
216
+ tags:
217
+ - tag: title
218
+ type: data
219
+ dataType: string
220
+ - tag: metadata
221
+ type: sub-contract
222
+ tags:
223
+ - tag: category
224
+ type: data
225
+ dataType: string
226
+ - tag: tags
227
+ type: data
228
+ dataType: string
229
+ - tag: relatedDocuments
230
+ type: sub-contract
231
+ repeated: true
232
+ link: $/metadata
233
+ ```
234
+
235
+ The `link: $/metadata` references the `metadata` sub-contract within the same contract.
236
+
237
+ **Generated TypeScript:**
238
+
239
+ ```typescript
240
+ export interface MetadataOfDocumentViewState {
241
+ category: string;
242
+ tags: string;
243
+ }
244
+
245
+ export interface DocumentViewState {
246
+ title: string;
247
+ metadata: MetadataOfDocumentViewState;
248
+ relatedDocuments: Array<MetadataOfDocumentViewState>;
249
+ }
250
+ ```
251
+
252
+ ### Array Item Unwrapping with `[]`
253
+
254
+ When a recursive link points to an array property, you can use the `[]` suffix to unwrap and link to the item type instead of the full array:
255
+
256
+ ```yaml
257
+ name: product-list
258
+ tags:
259
+ - tag: title
260
+ type: data
261
+ dataType: string
262
+ - tag: products
263
+ type: sub-contract
264
+ repeated: true
265
+ tags:
266
+ - tag: id
267
+ type: data
268
+ dataType: string
269
+ - tag: name
270
+ type: data
271
+ dataType: string
272
+ - tag: price
273
+ type: data
274
+ dataType: number
275
+ - tag: featuredProduct
276
+ type: sub-contract
277
+ link: $/products[]
278
+ description: Links to a single product item (not the array)
279
+ ```
280
+
281
+ **Without `[]` - Links to the Array:**
282
+
283
+ - `link: $/products` → `Array<ProductOfProductListViewState>`
284
+
285
+ **With `[]` - Links to the Array Item:**
286
+
287
+ - `link: $/products[]` → `ProductOfProductListViewState | null`
288
+
289
+ **Generated TypeScript:**
290
+
291
+ ```typescript
292
+ export interface ProductOfProductListViewState {
293
+ id: string;
294
+ name: string;
295
+ price: number;
296
+ }
297
+
298
+ export interface ProductListViewState {
299
+ title: string;
300
+ products: Array<ProductOfProductListViewState>;
301
+ featuredProduct: ProductOfProductListViewState | null;
302
+ }
303
+ ```
304
+
305
+ ### Recursive Link Syntax Reference
306
+
307
+ | Syntax | Resolves To | Generated Type |
308
+ | ------------------- | ---------------------- | --------------------------- |
309
+ | `$/` | Root contract | `ContractViewState \| null` |
310
+ | `$/propertyName` | Nested property | `PropertyType \| null` |
311
+ | `$/arrayProperty` | Array property | `Array<ItemType>` |
312
+ | `$/arrayProperty[]` | Array item type | `ItemType \| null` |
313
+ | `$/nested/path` | Deeply nested property | `PropertyType \| null` |
314
+
315
+ ### Type Nullability Rules
316
+
317
+ - **Non-array types** get `| null` since they may not exist
318
+ - **Array types** don't get `| null` since an empty array (`[]`) can be used
319
+ - **Repeated tags** with `repeated: true` always generate arrays
320
+
321
+ ### Common Use Cases
322
+
323
+ **1. Tree Structures (Direct Recursion)**
324
+
325
+ ```yaml
326
+ name: tree-node
327
+ tags:
328
+ - tag: value
329
+ type: data
330
+ dataType: string
331
+ - tag: children
332
+ type: sub-contract
333
+ repeated: true
334
+ link: $/
335
+ ```
336
+
337
+ **2. Linked Lists (Single Recursion)**
338
+
339
+ ```yaml
340
+ name: linked-list
341
+ tags:
342
+ - tag: value
343
+ type: data
344
+ dataType: string
345
+ - tag: next
346
+ type: sub-contract
347
+ link: $/
348
+ ```
349
+
350
+ **3. Nested Menus (Indirect Recursion)**
351
+
352
+ ```yaml
353
+ name: menu
354
+ tags:
355
+ - tag: label
356
+ type: data
357
+ dataType: string
358
+ - tag: submenu
359
+ type: sub-contract
360
+ tags:
361
+ - tag: items
362
+ type: sub-contract
363
+ repeated: true
364
+ link: $/
365
+ ```
366
+
367
+ **4. Featured Item from Array (Array Unwrapping)**
368
+
369
+ ```yaml
370
+ name: catalog
371
+ tags:
372
+ - tag: items
373
+ type: sub-contract
374
+ repeated: true
375
+ tags:
376
+ - tag: id
377
+ type: data
378
+ dataType: string
379
+ - tag: title
380
+ type: data
381
+ dataType: string
382
+ - tag: featured
383
+ type: sub-contract
384
+ link: $/items[]
385
+ ```
386
+
387
+ ### Best Practices for Recursive Links
388
+
389
+ 1. **Use descriptive paths** - Make it clear what you're referencing
390
+ 2. **Document recursion** - Add descriptions explaining the recursive relationship
391
+ 3. **Consider depth limits** - Be aware of potential deeply nested structures
392
+ 4. **Use `[]` for single items** - When referencing one item from an array, use `[]` unwrapping
393
+ 5. **Validate paths** - Ensure the referenced paths exist in your contract
394
+
181
395
  ## Data Types
182
396
 
183
397
  ### Basic Types
@@ -211,6 +211,67 @@ data:
211
211
 
212
212
  This creates a recursive type at `root.nested` where `children` has the same type as the parent `nested` object.
213
213
 
214
+ ### Array Recursion vs. Array Item References
215
+
216
+ When working with arrays, you can use two different syntaxes:
217
+
218
+ **Array Recursion** - Use `array<$/data/path>` to create arrays of recursive items:
219
+
220
+ ```yaml
221
+ data:
222
+ name: string
223
+ id: string
224
+ children: array<$/data>
225
+ ```
226
+
227
+ This creates an array where each item has the same type as the root data structure.
228
+
229
+ **Generated TypeScript:**
230
+
231
+ ```typescript
232
+ export interface TreeViewState {
233
+ name: string;
234
+ id: string;
235
+ children: Array<TreeViewState>;
236
+ }
237
+ ```
238
+
239
+ **Array Item Unwrapping** - Use `$/data/path[]` to reference a single item from an array property:
240
+
241
+ ```yaml
242
+ data:
243
+ products:
244
+ - id: string
245
+ name: string
246
+ price: number
247
+ featuredProduct: $/data/products[]
248
+ ```
249
+
250
+ The `[]` suffix unwraps the array and links to the item type instead of the full array.
251
+
252
+ **Generated TypeScript:**
253
+
254
+ ```typescript
255
+ export interface ProductOfProductListViewState {
256
+ id: string;
257
+ name: string;
258
+ price: number;
259
+ }
260
+
261
+ export interface ProductListViewState {
262
+ products: Array<ProductOfProductListViewState>;
263
+ featuredProduct: ProductOfProductListViewState | null;
264
+ }
265
+ ```
266
+
267
+ **Comparison:**
268
+
269
+ | Syntax | Use Case | Generated Type |
270
+ | -------------------- | ------------------------- | ------------------ |
271
+ | `array<$/data>` | Multiple items (array) | `Array<ItemType>` |
272
+ | `$/data/arrayProp` | Reference to entire array | `Array<ItemType>` |
273
+ | `$/data/arrayProp[]` | Single item from array | `ItemType \| null` |
274
+
214
275
  ### Creating Recursive Regions
215
276
 
216
277
  Use the `ref` attribute to mark an element as a recursive region, then use `<recurse>` to trigger recursion:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jay-framework/compiler-jay-html",
3
- "version": "0.8.0",
3
+ "version": "0.9.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.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",
37
+ "@jay-framework/compiler-analyze-exported-types": "^0.9.0",
38
+ "@jay-framework/compiler-shared": "^0.9.0",
39
+ "@jay-framework/component": "^0.9.0",
40
+ "@jay-framework/runtime": "^0.9.0",
41
+ "@jay-framework/secure": "^0.9.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.8.0",
54
- "@jay-framework/dev-environment": "^0.8.0",
53
+ "@jay-framework/4-react": "^0.9.0",
54
+ "@jay-framework/dev-environment": "^0.9.0",
55
55
  "@testing-library/jest-dom": "^6.2.0",
56
56
  "@types/js-beautify": "^1",
57
57
  "@types/node": "^20.11.5",
@@ -9,6 +9,7 @@ The recursive jay-html feature allows marking HTML subtrees as recursive regions
9
9
  - `ref="regionName"` on an element to mark it as a recursive region
10
10
  - `<recurse ref="regionName" />` to trigger recursion at that point
11
11
  - `array<$/data>` type syntax to define recursive type references
12
+ - `$/data/path[]` syntax to unwrap array items and reference the item type
12
13
 
13
14
  ## Test Cases
14
15
 
@@ -126,8 +127,17 @@ const render = (viewState: TreeViewState) =>
126
127
  2. **Type Consistency**: The array/property type must match the recursive type reference
127
128
  - Array recursion: `array<$/data>` → `Array<ViewState>`
128
129
  - Single recursion: `$/data` → `ViewState | null`
130
+ - Array reference: `$/data/arrayProp` → `Array<ItemType>`
131
+ - Array item unwrap: `$/data/arrayProp[]` → `ItemType | null`
129
132
  3. **Valid Region References**: `<recurse ref="name">` must reference an existing `ref="name"` element
130
133
  4. **Descendant Requirement**: `<recurse>` must be a descendant of the referenced region
134
+ 5. **Array Unwrap Validation**: The `[]` syntax must point to an actual array property
135
+
136
+ ## Type Nullability Rules
137
+
138
+ - **Non-array types** get `| null` since they may not exist
139
+ - **Array types** don't get `| null` since an empty array (`[]`) can be used
140
+ - **Repeated tags** with `repeated: true` always generate arrays
131
141
 
132
142
  ## Not Yet Tested
133
143
 
@@ -137,6 +147,5 @@ These fixtures do **not** yet test:
137
147
  - Referencing nested types (`array<$/data/metadata>`)
138
148
  - Error cases (missing guards, invalid references, etc.)
139
149
  - React target generation for recursive structures
140
- - Contract file recursion with `link: $/`
141
150
 
142
151
  Additional test fixtures should be added for these scenarios.