@barefootjs/cli 0.5.0 → 0.5.2
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.
- package/dist/docs/core/llms.txt +1 -0
- package/dist/docs/core/reactivity/batch.md +135 -0
- package/dist/docs/core/reactivity.md +2 -1
- package/dist/index.js +264 -57
- package/package.json +2 -2
package/dist/docs/core/llms.txt
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
## Reactivity
|
|
14
14
|
|
|
15
|
+
- [batch](https://barefootjs.dev/docs/reactivity/batch.md): Groups multiple signal writes so dependent effects and memos run once, after all writes complete.
|
|
15
16
|
- [createEffect](https://barefootjs.dev/docs/reactivity/create-effect.md): Runs a function and re-runs it whenever its tracked signal dependencies change.
|
|
16
17
|
- [createMemo](https://barefootjs.dev/docs/reactivity/create-memo.md): Creates a cached derived value that recomputes only when its dependencies change.
|
|
17
18
|
- [createSignal](https://barefootjs.dev/docs/reactivity/create-signal.md): Creates a reactive getter/setter pair for managing state.
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: batch
|
|
3
|
+
description: Groups multiple signal writes so dependent effects and memos run once, after all writes complete.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# batch
|
|
7
|
+
|
|
8
|
+
Groups multiple signal writes so that dependent effects and memos run **once**,
|
|
9
|
+
after all the writes inside the batch complete — instead of once per write.
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
import { batch } from '@barefootjs/client'
|
|
13
|
+
|
|
14
|
+
batch<T>(fn: () => T): T
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Returns the value produced by `fn`.
|
|
18
|
+
|
|
19
|
+
## Default behavior (no batch)
|
|
20
|
+
|
|
21
|
+
BarefootJS propagates updates **synchronously**: each setter call immediately
|
|
22
|
+
re-runs every subscriber. This keeps reads-after-writes predictable — after a
|
|
23
|
+
setter returns, derived memos, effects, and the DOM already reflect the new value.
|
|
24
|
+
|
|
25
|
+
The cost is that writing N signals that share a subscriber re-runs that
|
|
26
|
+
subscriber N times, and the subscriber briefly observes intermediate states
|
|
27
|
+
where some signals are updated and others are not:
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
const [x, setX] = createSignal(40)
|
|
31
|
+
const [y, setY] = createSignal(60)
|
|
32
|
+
|
|
33
|
+
createEffect(() => {
|
|
34
|
+
// depends on both x and y
|
|
35
|
+
send({ x: x(), y: y() })
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
setX(70) // effect runs — observes x=70, y=60 (intermediate)
|
|
39
|
+
setY(30) // effect runs again — observes x=70, y=30
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Beyond its initial run on creation, the effect ran twice more — once per write —
|
|
43
|
+
and saw a transient `x=70, y=60` state.
|
|
44
|
+
|
|
45
|
+
## With batch
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
batch(() => {
|
|
49
|
+
setX(70)
|
|
50
|
+
setY(30)
|
|
51
|
+
})
|
|
52
|
+
// effect runs once, observing x=70, y=30
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Inside `batch`, writes are collected and dependent subscribers are de-duplicated,
|
|
56
|
+
so each runs **exactly once** after the batch ends — and never observes an
|
|
57
|
+
intermediate, half-updated state.
|
|
58
|
+
|
|
59
|
+
## When to use
|
|
60
|
+
|
|
61
|
+
When a single handler updates several signals that feed shared effects/memos,
|
|
62
|
+
`batch` collapses the work into one update pass — and keeps the subscriber from
|
|
63
|
+
running while a cross-field invariant is temporarily broken:
|
|
64
|
+
|
|
65
|
+
```tsx
|
|
66
|
+
const reset = () => {
|
|
67
|
+
batch(() => {
|
|
68
|
+
setName('')
|
|
69
|
+
setEmail('')
|
|
70
|
+
setAge(0)
|
|
71
|
+
// ...20 more fields
|
|
72
|
+
})
|
|
73
|
+
// every subscriber ran once, not once-per-field
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Caveats
|
|
78
|
+
|
|
79
|
+
### Derived values are stale *inside* the batch
|
|
80
|
+
|
|
81
|
+
`batch` defers the work that recomputes derived values. Plain signal reads return
|
|
82
|
+
the new value immediately, but **memos and effect-driven values stay stale until
|
|
83
|
+
the batch ends**:
|
|
84
|
+
|
|
85
|
+
```tsx
|
|
86
|
+
const [n, setN] = createSignal(1)
|
|
87
|
+
const doubled = createMemo(() => n() * 2)
|
|
88
|
+
|
|
89
|
+
batch(() => {
|
|
90
|
+
setN(10)
|
|
91
|
+
n() // 10 — plain signal read is fresh
|
|
92
|
+
doubled() // 2 — STALE; the memo hasn't recomputed yet
|
|
93
|
+
})
|
|
94
|
+
doubled() // 20 — recomputed after the batch ends
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
If you need the recomputed value, read it after the batch.
|
|
98
|
+
|
|
99
|
+
### `await` escapes the batch
|
|
100
|
+
|
|
101
|
+
`batch` only covers the **synchronous** portion of `fn`. Wrapping an async
|
|
102
|
+
function in `batch` groups only the writes before the first `await` — everything
|
|
103
|
+
after runs ungrouped, and the promise `batch` returns is easy to leave floating.
|
|
104
|
+
|
|
105
|
+
Instead, wrap each synchronous group of writes in its own `batch`, with `await`
|
|
106
|
+
between the groups:
|
|
107
|
+
|
|
108
|
+
```tsx
|
|
109
|
+
const onSubmit = async () => {
|
|
110
|
+
batch(() => {
|
|
111
|
+
setLoading(true)
|
|
112
|
+
setError(null)
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
const result = await save()
|
|
117
|
+
batch(() => {
|
|
118
|
+
setLoading(false)
|
|
119
|
+
setResult(result)
|
|
120
|
+
})
|
|
121
|
+
} catch (err) {
|
|
122
|
+
batch(() => {
|
|
123
|
+
setLoading(false)
|
|
124
|
+
setError(err)
|
|
125
|
+
})
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Note
|
|
131
|
+
|
|
132
|
+
`batch` is an **opt-in** optimization. Forgetting it is never a correctness bug —
|
|
133
|
+
code still works, just with extra subscriber runs. Reach for `batch` when a
|
|
134
|
+
handler writes many signals that share subscribers, or when an effect must not
|
|
135
|
+
observe a partially-updated state.
|
|
@@ -9,7 +9,7 @@ All reactive primitives are imported from `@barefootjs/client`:
|
|
|
9
9
|
|
|
10
10
|
```tsx
|
|
11
11
|
"use client"
|
|
12
|
-
import { createSignal, createEffect, createMemo, onMount, onCleanup, untrack } from '@barefootjs/client'
|
|
12
|
+
import { createSignal, createEffect, createMemo, onMount, onCleanup, untrack, batch } from '@barefootjs/client'
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
## API Reference
|
|
@@ -22,6 +22,7 @@ import { createSignal, createEffect, createMemo, onMount, onCleanup, untrack } f
|
|
|
22
22
|
| [`onMount`](./reactivity/on-mount.md) | Run once on component initialization |
|
|
23
23
|
| [`onCleanup`](./reactivity/on-cleanup.md) | Register cleanup for effects and lifecycle |
|
|
24
24
|
| [`untrack`](./reactivity/untrack.md) | Read signals without tracking dependencies |
|
|
25
|
+
| [`batch`](./reactivity/batch.md) | Group signal writes so subscribers run once |
|
|
25
26
|
|
|
26
27
|
## Guides
|
|
27
28
|
|
package/dist/index.js
CHANGED
|
@@ -359,13 +359,16 @@ var init_js_scanner = __esm({
|
|
|
359
359
|
});
|
|
360
360
|
|
|
361
361
|
// ../shared/src/markers.ts
|
|
362
|
+
function loopItemMarker(key) {
|
|
363
|
+
return `${BF_LOOP_ITEM}:${key}`;
|
|
364
|
+
}
|
|
362
365
|
function loopStartMarker(markerId) {
|
|
363
366
|
return `${BF_LOOP_START}:${markerId}`;
|
|
364
367
|
}
|
|
365
368
|
function loopEndMarker(markerId) {
|
|
366
369
|
return `${BF_LOOP_END}:${markerId}`;
|
|
367
370
|
}
|
|
368
|
-
var BF_SCOPE, BF_SLOT, BF_HOST, BF_AT, BF_COND, BF_LOOP_START, BF_LOOP_END, BF_KEY, BF_KEY_PREFIX, BF_PLACEHOLDER, BF_PARENT_SCOPE_PLACEHOLDER;
|
|
371
|
+
var BF_SCOPE, BF_SLOT, BF_HOST, BF_AT, BF_COND, BF_LOOP_START, BF_LOOP_END, BF_LOOP_ITEM, BF_KEY, BF_KEY_PREFIX, BF_PLACEHOLDER, BF_PARENT_SCOPE_PLACEHOLDER;
|
|
369
372
|
var init_markers = __esm({
|
|
370
373
|
"../shared/src/markers.ts"() {
|
|
371
374
|
"use strict";
|
|
@@ -376,6 +379,7 @@ var init_markers = __esm({
|
|
|
376
379
|
BF_COND = "bf-c";
|
|
377
380
|
BF_LOOP_START = "bf-loop";
|
|
378
381
|
BF_LOOP_END = "bf-/loop";
|
|
382
|
+
BF_LOOP_ITEM = "bf-loop-i";
|
|
379
383
|
BF_KEY = "data-key";
|
|
380
384
|
BF_KEY_PREFIX = "data-key-";
|
|
381
385
|
BF_PLACEHOLDER = "data-bf-ph";
|
|
@@ -1376,6 +1380,9 @@ function buildSpreadAttrsMergeCall(args2) {
|
|
|
1376
1380
|
}
|
|
1377
1381
|
return `\${spreadAttrs({${objMembers.join(", ")}})}`;
|
|
1378
1382
|
}
|
|
1383
|
+
function itemAnchorTemplate(keyExpr) {
|
|
1384
|
+
return `<!--${loopItemMarker("${" + keyExpr + "}")}-->`;
|
|
1385
|
+
}
|
|
1379
1386
|
function irToHtmlTemplate(node, restSpreadNames, loopDepth = 0, loopParams, branchSlotsVar, insideLoop = false, inHoistedChildren = false) {
|
|
1380
1387
|
const recurse = (n) => irToHtmlTemplate(n, restSpreadNames, loopDepth, loopParams, branchSlotsVar, insideLoop, inHoistedChildren);
|
|
1381
1388
|
const wrapExpr = (expr) => wrapExprWithLoopParams(expr, loopParams);
|
|
@@ -1467,7 +1474,10 @@ function irToHtmlTemplate(node, restSpreadNames, loopDepth = 0, loopParams, bran
|
|
|
1467
1474
|
}
|
|
1468
1475
|
case "loop": {
|
|
1469
1476
|
const innerRecurse = (n) => irToHtmlTemplate(n, restSpreadNames, loopDepth + 1, loopParams, branchSlotsVar, insideLoop);
|
|
1470
|
-
|
|
1477
|
+
let childTemplate = node.children.map(innerRecurse).join("");
|
|
1478
|
+
if (node.bodyIsItemConditional && node.key) {
|
|
1479
|
+
childTemplate = `${itemAnchorTemplate(node.key)}${childTemplate}`;
|
|
1480
|
+
}
|
|
1471
1481
|
const indexParam = node.index ? `, ${node.index}` : "";
|
|
1472
1482
|
const rawChainedArray = applyLoopChain(node);
|
|
1473
1483
|
const { array: iterArray, callbackParam } = applyIterationShape(node, rawChainedArray, indexParam);
|
|
@@ -2030,7 +2040,10 @@ function generateCsrTemplateWithOpts(node, opts) {
|
|
|
2030
2040
|
return `\${renderChild('${nameForRegistryRef(node.name)}', ${propsExpr}${keyArg || (slotArg ? ", undefined" : "")}${slotArg})}`;
|
|
2031
2041
|
}
|
|
2032
2042
|
case "loop": {
|
|
2033
|
-
|
|
2043
|
+
let childTemplate = node.children.map(recurseInLoop).join("");
|
|
2044
|
+
if (node.bodyIsItemConditional && node.key) {
|
|
2045
|
+
childTemplate = `${itemAnchorTemplate(node.key)}${childTemplate}`;
|
|
2046
|
+
}
|
|
2034
2047
|
const indexParam = node.index ? `, ${node.index}` : "";
|
|
2035
2048
|
const chainedTemplateArray = node.sortComparator || node.filterPredicate ? applyLoopChain(node, node.templateArray) : node.templateArray;
|
|
2036
2049
|
const rawArrayExpr = transformExpr(node.array, chainedTemplateArray);
|
|
@@ -2437,6 +2450,24 @@ function getSourceLocation(node, sourceFile, filePath) {
|
|
|
2437
2450
|
}
|
|
2438
2451
|
};
|
|
2439
2452
|
}
|
|
2453
|
+
function membersToProperties(members, sourceFile) {
|
|
2454
|
+
return members.filter(ts6.isPropertySignature).map((member) => ({
|
|
2455
|
+
name: propertyNameText(member.name, sourceFile),
|
|
2456
|
+
type: typeNodeToTypeInfo(member.type, sourceFile) ?? {
|
|
2457
|
+
kind: "unknown",
|
|
2458
|
+
raw: "unknown"
|
|
2459
|
+
},
|
|
2460
|
+
optional: !!member.questionToken,
|
|
2461
|
+
readonly: !!member.modifiers?.some(
|
|
2462
|
+
(m) => m.kind === ts6.SyntaxKind.ReadonlyKeyword
|
|
2463
|
+
)
|
|
2464
|
+
}));
|
|
2465
|
+
}
|
|
2466
|
+
function propertyNameText(name, sourceFile) {
|
|
2467
|
+
if (!name) return "";
|
|
2468
|
+
if (ts6.isStringLiteral(name) || ts6.isNumericLiteral(name)) return name.text;
|
|
2469
|
+
return name.getText(sourceFile);
|
|
2470
|
+
}
|
|
2440
2471
|
function typeNodeToTypeInfo(typeNode, sourceFile) {
|
|
2441
2472
|
if (!typeNode) return null;
|
|
2442
2473
|
const raw = typeNode.getText(sourceFile);
|
|
@@ -2475,20 +2506,21 @@ function typeNodeToTypeInfo(typeNode, sourceFile) {
|
|
|
2475
2506
|
return {
|
|
2476
2507
|
kind: "object",
|
|
2477
2508
|
raw,
|
|
2478
|
-
properties: typeNode.members
|
|
2479
|
-
name: member.name?.getText(sourceFile) ?? "",
|
|
2480
|
-
type: typeNodeToTypeInfo(member.type, sourceFile) ?? {
|
|
2481
|
-
kind: "unknown",
|
|
2482
|
-
raw: "unknown"
|
|
2483
|
-
},
|
|
2484
|
-
optional: !!member.questionToken,
|
|
2485
|
-
readonly: !!member.modifiers?.some(
|
|
2486
|
-
(m) => m.kind === ts6.SyntaxKind.ReadonlyKeyword
|
|
2487
|
-
)
|
|
2488
|
-
}))
|
|
2509
|
+
properties: membersToProperties(typeNode.members, sourceFile)
|
|
2489
2510
|
};
|
|
2490
2511
|
}
|
|
2491
2512
|
if (ts6.isTypeReferenceNode(typeNode)) {
|
|
2513
|
+
const refName = ts6.isIdentifier(typeNode.typeName) ? typeNode.typeName.text : "";
|
|
2514
|
+
if ((refName === "Array" || refName === "ReadonlyArray") && typeNode.typeArguments?.length === 1) {
|
|
2515
|
+
return {
|
|
2516
|
+
kind: "array",
|
|
2517
|
+
raw,
|
|
2518
|
+
elementType: typeNodeToTypeInfo(typeNode.typeArguments[0], sourceFile) ?? {
|
|
2519
|
+
kind: "unknown",
|
|
2520
|
+
raw: "unknown"
|
|
2521
|
+
}
|
|
2522
|
+
};
|
|
2523
|
+
}
|
|
2492
2524
|
return {
|
|
2493
2525
|
kind: "interface",
|
|
2494
2526
|
raw
|
|
@@ -3666,14 +3698,17 @@ function collectInterfaceDefinition(node, ctx2) {
|
|
|
3666
3698
|
kind: "interface",
|
|
3667
3699
|
name: node.name.text,
|
|
3668
3700
|
definition: node.getText(ctx2.sourceFile),
|
|
3701
|
+
properties: membersToProperties(node.members, ctx2.sourceFile),
|
|
3669
3702
|
loc: getSourceLocation(node, ctx2.sourceFile, ctx2.filePath)
|
|
3670
3703
|
});
|
|
3671
3704
|
}
|
|
3672
3705
|
function collectTypeAliasDefinition(node, ctx2) {
|
|
3706
|
+
const properties = ts7.isTypeLiteralNode(node.type) ? membersToProperties(node.type.members, ctx2.sourceFile) : void 0;
|
|
3673
3707
|
ctx2.typeDefinitions.push({
|
|
3674
3708
|
kind: "type",
|
|
3675
3709
|
name: node.name.text,
|
|
3676
3710
|
definition: node.getText(ctx2.sourceFile),
|
|
3711
|
+
properties,
|
|
3677
3712
|
loc: getSourceLocation(node, ctx2.sourceFile, ctx2.filePath)
|
|
3678
3713
|
});
|
|
3679
3714
|
}
|
|
@@ -5869,7 +5904,7 @@ function checkSupport(expr) {
|
|
|
5869
5904
|
return {
|
|
5870
5905
|
supported: false,
|
|
5871
5906
|
level: "L5_UNSUPPORTED",
|
|
5872
|
-
reason: `
|
|
5907
|
+
reason: `Method '${methodName}()' has no template lowering and requires client-side evaluation. Wrap the expression in /* @client */ to defer it to hydration, or pre-compute the value before rendering.`
|
|
5873
5908
|
};
|
|
5874
5909
|
}
|
|
5875
5910
|
}
|
|
@@ -6159,7 +6194,7 @@ var init_expression_parser = __esm({
|
|
|
6159
6194
|
"some",
|
|
6160
6195
|
"forEach",
|
|
6161
6196
|
"flatMap",
|
|
6162
|
-
"flat"
|
|
6197
|
+
"flat",
|
|
6163
6198
|
// #1448 Tier A — Array methods. Each method PR adds the lowering
|
|
6164
6199
|
// (typically a new `array-method` variant or runtime helper) and
|
|
6165
6200
|
// removes its row here. See packages/adapter-tests/fixtures/methods/.
|
|
@@ -6189,6 +6224,34 @@ var init_expression_parser = __esm({
|
|
|
6189
6224
|
// `bf_lower` / `bf_upper` (Go) and Perl's native `lc` / `uc` (Mojo).
|
|
6190
6225
|
// `trim` lowers via the `array-method` IR + `bf_trim` (Go) and a
|
|
6191
6226
|
// Perl regex strip (Mojo).
|
|
6227
|
+
//
|
|
6228
|
+
// #1448 follow-up — String methods that have NO lowering yet. These
|
|
6229
|
+
// were previously absent from this gate, so `isSupported` reported
|
|
6230
|
+
// them "supported" and the adapters emitted a raw method call
|
|
6231
|
+
// (`{{.Name.StartsWith "a"}}` on Go, `$name->{startsWith}('a')` on
|
|
6232
|
+
// Mojo) with no build diagnostic — a silent footgun that only
|
|
6233
|
+
// surfaced as a crash at template-render time. Listing them here
|
|
6234
|
+
// makes the build fail loudly with BF101 (the same treatment the
|
|
6235
|
+
// unsupported array methods above get), pointing users at the
|
|
6236
|
+
// `/* @client */` escape hatch. Each name drops off as its lowering
|
|
6237
|
+
// lands. See #1448 "Unsupported string methods" Tier B / Tier C.
|
|
6238
|
+
"split",
|
|
6239
|
+
"startsWith",
|
|
6240
|
+
"endsWith",
|
|
6241
|
+
"replace",
|
|
6242
|
+
"replaceAll",
|
|
6243
|
+
"repeat",
|
|
6244
|
+
"padStart",
|
|
6245
|
+
"padEnd",
|
|
6246
|
+
"charAt",
|
|
6247
|
+
"charCodeAt",
|
|
6248
|
+
"codePointAt",
|
|
6249
|
+
"normalize",
|
|
6250
|
+
"substring",
|
|
6251
|
+
"substr",
|
|
6252
|
+
"match",
|
|
6253
|
+
"matchAll",
|
|
6254
|
+
"search"
|
|
6192
6255
|
]);
|
|
6193
6256
|
}
|
|
6194
6257
|
});
|
|
@@ -7385,6 +7448,22 @@ function containsJsxInExpression(node) {
|
|
|
7385
7448
|
}
|
|
7386
7449
|
return ts11.forEachChild(node, containsJsxInExpression) ?? false;
|
|
7387
7450
|
}
|
|
7451
|
+
function callsJsxHelper(node, ctx2) {
|
|
7452
|
+
let found = false;
|
|
7453
|
+
const visit3 = (n) => {
|
|
7454
|
+
if (found) return;
|
|
7455
|
+
if (ts11.isCallExpression(n) && ts11.isIdentifier(n.expression)) {
|
|
7456
|
+
const name = n.expression.text;
|
|
7457
|
+
if (ctx2.analyzer.jsxFunctions.has(name) || ctx2.analyzer.jsxMultiReturnFunctions.has(name)) {
|
|
7458
|
+
found = true;
|
|
7459
|
+
return;
|
|
7460
|
+
}
|
|
7461
|
+
}
|
|
7462
|
+
ts11.forEachChild(n, visit3);
|
|
7463
|
+
};
|
|
7464
|
+
visit3(node);
|
|
7465
|
+
return found;
|
|
7466
|
+
}
|
|
7388
7467
|
function containsAwaitExpression(node) {
|
|
7389
7468
|
if (ts11.isAwaitExpression(node)) return true;
|
|
7390
7469
|
if (ts11.isFunctionDeclaration(node) || ts11.isFunctionExpression(node) || ts11.isArrowFunction(node)) return false;
|
|
@@ -7466,7 +7545,7 @@ function transformJsxExpression(expr, ctx2, isClientOnly = false) {
|
|
|
7466
7545
|
if (node.operatorToken.kind === ts11.SyntaxKind.AmpersandAmpersandToken) {
|
|
7467
7546
|
return transformLogicalAnd(node, ctx2);
|
|
7468
7547
|
}
|
|
7469
|
-
if ((node.operatorToken.kind === ts11.SyntaxKind.QuestionQuestionToken || node.operatorToken.kind === ts11.SyntaxKind.BarBarToken) && containsJsxInExpression(node.right)) {
|
|
7548
|
+
if ((node.operatorToken.kind === ts11.SyntaxKind.QuestionQuestionToken || node.operatorToken.kind === ts11.SyntaxKind.BarBarToken) && (containsJsxInExpression(node.right) || callsJsxHelper(node.right, ctx2))) {
|
|
7470
7549
|
return transformNullishCoalescing(node, ctx2);
|
|
7471
7550
|
}
|
|
7472
7551
|
return null;
|
|
@@ -7906,17 +7985,20 @@ function checkLoopKey(callback, ctx2, isNested) {
|
|
|
7906
7985
|
body = ret.expression;
|
|
7907
7986
|
}
|
|
7908
7987
|
while (ts11.isParenthesizedExpression(body)) body = body.expression;
|
|
7988
|
+
function checkJsxOperand(node) {
|
|
7989
|
+
let n = node;
|
|
7990
|
+
while (ts11.isParenthesizedExpression(n)) n = n.expression;
|
|
7991
|
+
if (ts11.isJsxElement(n)) checkOpening(n.openingElement);
|
|
7992
|
+
else if (ts11.isJsxSelfClosingElement(n)) checkOpening(n);
|
|
7993
|
+
}
|
|
7909
7994
|
if (ts11.isConditionalExpression(body)) {
|
|
7910
|
-
|
|
7911
|
-
|
|
7912
|
-
|
|
7913
|
-
|
|
7914
|
-
|
|
7915
|
-
|
|
7916
|
-
|
|
7917
|
-
else if (ts11.isJsxSelfClosingElement(wt)) checkOpening(wt);
|
|
7918
|
-
if (ts11.isJsxElement(wf)) checkOpening(wf.openingElement);
|
|
7919
|
-
else if (ts11.isJsxSelfClosingElement(wf)) checkOpening(wf);
|
|
7995
|
+
checkJsxOperand(body.whenTrue);
|
|
7996
|
+
checkJsxOperand(body.whenFalse);
|
|
7997
|
+
return;
|
|
7998
|
+
}
|
|
7999
|
+
if (ts11.isBinaryExpression(body) && (body.operatorToken.kind === ts11.SyntaxKind.AmpersandAmpersandToken || body.operatorToken.kind === ts11.SyntaxKind.BarBarToken || body.operatorToken.kind === ts11.SyntaxKind.QuestionQuestionToken)) {
|
|
8000
|
+
checkJsxOperand(body.left);
|
|
8001
|
+
checkJsxOperand(body.right);
|
|
7920
8002
|
return;
|
|
7921
8003
|
}
|
|
7922
8004
|
if (ts11.isJsxElement(body)) {
|
|
@@ -7938,6 +8020,39 @@ function loopBodyIsMultiRoot(children) {
|
|
|
7938
8020
|
if (only.type !== "fragment") return false;
|
|
7939
8021
|
return loopBodyIsMultiRoot(only.children);
|
|
7940
8022
|
}
|
|
8023
|
+
function branchHasNoElement(node) {
|
|
8024
|
+
if (node.type === "element" || node.type === "component") return false;
|
|
8025
|
+
if (node.type === "conditional") {
|
|
8026
|
+
return branchHasNoElement(node.whenTrue) || branchHasNoElement(node.whenFalse);
|
|
8027
|
+
}
|
|
8028
|
+
if (node.type === "fragment") {
|
|
8029
|
+
const real = node.children.filter(
|
|
8030
|
+
(c) => !(c.type === "text" && typeof c.value === "string" && !c.value.trim())
|
|
8031
|
+
);
|
|
8032
|
+
return real.length !== 1 || branchHasNoElement(real[0]);
|
|
8033
|
+
}
|
|
8034
|
+
return true;
|
|
8035
|
+
}
|
|
8036
|
+
function loopBodyItemConditional(children) {
|
|
8037
|
+
const real = children.filter(
|
|
8038
|
+
(c) => !(c.type === "text" && typeof c.value === "string" && !c.value.trim())
|
|
8039
|
+
);
|
|
8040
|
+
if (real.length !== 1) return null;
|
|
8041
|
+
const only = real[0];
|
|
8042
|
+
if (only.type !== "conditional") return null;
|
|
8043
|
+
if (branchHasNoElement(only.whenTrue) || branchHasNoElement(only.whenFalse)) {
|
|
8044
|
+
return only;
|
|
8045
|
+
}
|
|
8046
|
+
return null;
|
|
8047
|
+
}
|
|
8048
|
+
function extractItemConditionalKey(cond) {
|
|
8049
|
+
const a = branchHasNoElement(cond.whenTrue) ? null : extractLoopKey(cond.whenTrue);
|
|
8050
|
+
const b = branchHasNoElement(cond.whenFalse) ? null : extractLoopKey(cond.whenFalse);
|
|
8051
|
+
if (a !== null && b !== null) {
|
|
8052
|
+
return normalizeKeyExpr(a) === normalizeKeyExpr(b) ? a : null;
|
|
8053
|
+
}
|
|
8054
|
+
return a ?? b;
|
|
8055
|
+
}
|
|
7941
8056
|
function transformMapCall(node, ctx2, isClientOnly = false, method = "map") {
|
|
7942
8057
|
const isNested = ctx2.loopParams.size > 0;
|
|
7943
8058
|
const propAccess = node.expression;
|
|
@@ -8134,6 +8249,16 @@ function transformMapCall(node, ctx2, isClientOnly = false, method = "map") {
|
|
|
8134
8249
|
ctx2.loopParams.add(param);
|
|
8135
8250
|
}
|
|
8136
8251
|
if (index) ctx2.loopParams.add(index);
|
|
8252
|
+
const tryTransformRenderableBody = (expr) => {
|
|
8253
|
+
if (!ts11.isBinaryExpression(expr)) return;
|
|
8254
|
+
const op = expr.operatorToken.kind;
|
|
8255
|
+
if (op !== ts11.SyntaxKind.AmpersandAmpersandToken && op !== ts11.SyntaxKind.BarBarToken && op !== ts11.SyntaxKind.QuestionQuestionToken) {
|
|
8256
|
+
return;
|
|
8257
|
+
}
|
|
8258
|
+
if (!containsJsxInExpression(expr) && !callsJsxHelper(expr, ctx2)) return;
|
|
8259
|
+
const transformed = transformJsxExpression(expr, ctx2, isClientOnly);
|
|
8260
|
+
if (transformed) children = [transformed];
|
|
8261
|
+
};
|
|
8137
8262
|
const body = callback.body;
|
|
8138
8263
|
if (ts11.isJsxElement(body) || ts11.isJsxSelfClosingElement(body) || ts11.isJsxFragment(body)) {
|
|
8139
8264
|
const transformed = transformNode(body, ctx2);
|
|
@@ -8156,6 +8281,8 @@ function transformMapCall(node, ctx2, isClientOnly = false, method = "map") {
|
|
|
8156
8281
|
children = [transformConditional(inner, ctx2)];
|
|
8157
8282
|
} else if (method === "flatMap" && ts11.isArrayLiteralExpression(inner)) {
|
|
8158
8283
|
children = transformArrayLiteralChildren(inner, ctx2);
|
|
8284
|
+
} else {
|
|
8285
|
+
tryTransformRenderableBody(inner);
|
|
8159
8286
|
}
|
|
8160
8287
|
} else if (method === "flatMap" && ts11.isArrayLiteralExpression(body)) {
|
|
8161
8288
|
children = transformArrayLiteralChildren(body, ctx2);
|
|
@@ -8203,6 +8330,8 @@ function transformMapCall(node, ctx2, isClientOnly = false, method = "map") {
|
|
|
8203
8330
|
if (method === "flatMap" && children.length === 0) {
|
|
8204
8331
|
flatMapCallback = buildFlatMapCallback(callback, body, ctx2);
|
|
8205
8332
|
}
|
|
8333
|
+
} else {
|
|
8334
|
+
tryTransformRenderableBody(body);
|
|
8206
8335
|
}
|
|
8207
8336
|
if (paramBindings) {
|
|
8208
8337
|
for (const b of paramBindings) ctx2.loopParams.delete(b.name);
|
|
@@ -8217,7 +8346,9 @@ function transformMapCall(node, ctx2, isClientOnly = false, method = "map") {
|
|
|
8217
8346
|
if (ts11.isArrowFunction(node.arguments[0]) && children.length > 0) {
|
|
8218
8347
|
checkLoopKey(node.arguments[0], ctx2, isNested);
|
|
8219
8348
|
}
|
|
8220
|
-
const
|
|
8349
|
+
const itemConditional = children.length > 0 ? loopBodyItemConditional(children) : null;
|
|
8350
|
+
const bodyIsItemConditional = itemConditional !== null;
|
|
8351
|
+
const key = bodyIsItemConditional ? extractItemConditionalKey(itemConditional) : children.length > 0 ? extractLoopKey(children[0]) : null;
|
|
8221
8352
|
let childComponent;
|
|
8222
8353
|
if (children.length === 1 && children[0].type === "component") {
|
|
8223
8354
|
const comp = children[0];
|
|
@@ -8263,6 +8394,7 @@ function transformMapCall(node, ctx2, isClientOnly = false, method = "map") {
|
|
|
8263
8394
|
callsReactiveGetters: callsReactive || void 0,
|
|
8264
8395
|
hasFunctionCalls: hasCalls || void 0,
|
|
8265
8396
|
bodyIsMultiRoot: bodyIsMultiRoot || void 0,
|
|
8397
|
+
bodyIsItemConditional: bodyIsItemConditional || void 0,
|
|
8266
8398
|
childComponent,
|
|
8267
8399
|
nestedComponents,
|
|
8268
8400
|
filterPredicate,
|
|
@@ -9433,7 +9565,8 @@ function collectLoopChildReactiveAttrs(node, ctx2, loopParam, loopParamBindings)
|
|
|
9433
9565
|
const valueStr = attrValueToString(attr.value);
|
|
9434
9566
|
if (!valueStr) continue;
|
|
9435
9567
|
const expanded = expandConstantForReactivity(valueStr, ctx2, attr.freeIdentifiers);
|
|
9436
|
-
|
|
9568
|
+
const reactive = classifyReactivity(expanded.expr, ctx2, loopParam, loopParamBindings, expanded.freeIds).kind !== "none" || attr.callsReactiveGetters || attr.hasFunctionCalls;
|
|
9569
|
+
if (!attr.clientOnly && !reactive) continue;
|
|
9437
9570
|
attrs.push({
|
|
9438
9571
|
childSlotId: el.slotId,
|
|
9439
9572
|
attrName: attr.name,
|
|
@@ -9462,21 +9595,30 @@ function producesDomChild(node) {
|
|
|
9462
9595
|
}
|
|
9463
9596
|
function computeLoopSiblingOffsets(root) {
|
|
9464
9597
|
const offsets = /* @__PURE__ */ new Map();
|
|
9465
|
-
|
|
9466
|
-
|
|
9467
|
-
|
|
9468
|
-
|
|
9469
|
-
if (
|
|
9470
|
-
|
|
9471
|
-
|
|
9472
|
-
nonLoopCount++;
|
|
9473
|
-
}
|
|
9598
|
+
const recordChildren = (children) => {
|
|
9599
|
+
let nonLoopCount = 0;
|
|
9600
|
+
for (const child of children) {
|
|
9601
|
+
if (child.type === "loop") {
|
|
9602
|
+
if (nonLoopCount > 0) offsets.set(child, nonLoopCount);
|
|
9603
|
+
} else if (producesDomChild(child)) {
|
|
9604
|
+
nonLoopCount++;
|
|
9474
9605
|
}
|
|
9475
|
-
descend();
|
|
9476
9606
|
}
|
|
9477
|
-
|
|
9478
|
-
|
|
9479
|
-
|
|
9607
|
+
};
|
|
9608
|
+
const containerVisit = ({ node, descend }) => {
|
|
9609
|
+
recordChildren(node.children);
|
|
9610
|
+
descend();
|
|
9611
|
+
};
|
|
9612
|
+
walkIR(root, null, {
|
|
9613
|
+
element: containerVisit,
|
|
9614
|
+
component: containerVisit,
|
|
9615
|
+
fragment: containerVisit,
|
|
9616
|
+
provider: containerVisit,
|
|
9617
|
+
async: containerVisit
|
|
9618
|
+
// `loop` / `conditional` / `if-statement` are not flat sibling
|
|
9619
|
+
// containers (their children are item bodies / branches), and leaves
|
|
9620
|
+
// (text / expression / slot) have no children — all rely on walkIR's
|
|
9621
|
+
// default descent with the same scope.
|
|
9480
9622
|
});
|
|
9481
9623
|
return offsets;
|
|
9482
9624
|
}
|
|
@@ -9557,6 +9699,7 @@ function collectInnerLoops(nodes, siblingOffsets, outerLoopParam, ctx2, options)
|
|
|
9557
9699
|
key: n.key,
|
|
9558
9700
|
markerId: n.markerId,
|
|
9559
9701
|
bodyIsMultiRoot: n.bodyIsMultiRoot,
|
|
9702
|
+
bodyIsItemConditional: n.bodyIsItemConditional,
|
|
9560
9703
|
iterationShape: n.iterationShape,
|
|
9561
9704
|
containerSlotId: scope.parentSlotId,
|
|
9562
9705
|
template,
|
|
@@ -9763,6 +9906,7 @@ function collectElements(node, ctx2, siblingOffsets, insideConditional = false)
|
|
|
9763
9906
|
key: l.key,
|
|
9764
9907
|
markerId: l.markerId,
|
|
9765
9908
|
bodyIsMultiRoot: l.bodyIsMultiRoot,
|
|
9909
|
+
bodyIsItemConditional: l.bodyIsItemConditional,
|
|
9766
9910
|
iterationShape: l.iterationShape,
|
|
9767
9911
|
template,
|
|
9768
9912
|
staticItemTemplate,
|
|
@@ -9956,6 +10100,7 @@ function collectBranchLoops(node, ctx2, siblingOffsets) {
|
|
|
9956
10100
|
key: n.key,
|
|
9957
10101
|
markerId: n.markerId,
|
|
9958
10102
|
bodyIsMultiRoot: n.bodyIsMultiRoot,
|
|
10103
|
+
bodyIsItemConditional: n.bodyIsItemConditional,
|
|
9959
10104
|
iterationShape: n.iterationShape,
|
|
9960
10105
|
template: childTemplate,
|
|
9961
10106
|
containerSlotId: containerSlot,
|
|
@@ -10624,6 +10769,7 @@ var init_imports = __esm({
|
|
|
10624
10769
|
"getLoopChildren",
|
|
10625
10770
|
"getLoopNodes",
|
|
10626
10771
|
"mapArray",
|
|
10772
|
+
"mapArrayAnchored",
|
|
10627
10773
|
"createDisposableEffect",
|
|
10628
10774
|
"createComponent",
|
|
10629
10775
|
"renderChild",
|
|
@@ -10647,7 +10793,8 @@ var init_imports = __esm({
|
|
|
10647
10793
|
"qsaChildScopes",
|
|
10648
10794
|
"upsertChildItem",
|
|
10649
10795
|
"__slot",
|
|
10650
|
-
"__bfSlot"
|
|
10796
|
+
"__bfSlot",
|
|
10797
|
+
"__bfText"
|
|
10651
10798
|
];
|
|
10652
10799
|
RUNTIME_MODULE = "@barefootjs/client/runtime";
|
|
10653
10800
|
IMPORT_PLACEHOLDER = "/* __BAREFOOTJS_DOM_IMPORTS__ */";
|
|
@@ -13528,17 +13675,21 @@ function emitDynamicTextUpdates(lines, ctx2) {
|
|
|
13528
13675
|
const conditionalElems = elems.filter((e) => e.insideConditional);
|
|
13529
13676
|
const normalElems = elems.filter((e) => !e.insideConditional);
|
|
13530
13677
|
if (normalElems.length > 0 || conditionalElems.length > 0) {
|
|
13678
|
+
for (const elem of normalElems) {
|
|
13679
|
+
const v = varSlotId(elem.slotId);
|
|
13680
|
+
lines.push(` let __anchor_${v} = _${v}`);
|
|
13681
|
+
}
|
|
13531
13682
|
lines.push(` createEffect(() => {`);
|
|
13532
13683
|
if (normalElems.length > 0) {
|
|
13533
13684
|
lines.push(` const __val = ${expr}`);
|
|
13534
13685
|
for (const elem of normalElems) {
|
|
13535
13686
|
const v = varSlotId(elem.slotId);
|
|
13536
|
-
lines.push(`
|
|
13687
|
+
lines.push(` __anchor_${v} = __bfText(__anchor_${v}, __val)`);
|
|
13537
13688
|
}
|
|
13538
13689
|
for (const elem of conditionalElems) {
|
|
13539
13690
|
const v = varSlotId(elem.slotId);
|
|
13540
13691
|
lines.push(` const [__el_${v}] = $t(__scope, '${elem.slotId}')`);
|
|
13541
|
-
lines.push(`
|
|
13692
|
+
lines.push(` __bfText(__el_${v}, __val)`);
|
|
13542
13693
|
}
|
|
13543
13694
|
} else {
|
|
13544
13695
|
lines.push(` let __val`);
|
|
@@ -13546,7 +13697,7 @@ function emitDynamicTextUpdates(lines, ctx2) {
|
|
|
13546
13697
|
for (const elem of conditionalElems) {
|
|
13547
13698
|
const v = varSlotId(elem.slotId);
|
|
13548
13699
|
lines.push(` const [__el_${v}] = $t(__scope, '${elem.slotId}')`);
|
|
13549
|
-
lines.push(`
|
|
13700
|
+
lines.push(` __bfText(__el_${v}, __val)`);
|
|
13550
13701
|
}
|
|
13551
13702
|
}
|
|
13552
13703
|
lines.push(` })`);
|
|
@@ -14079,8 +14230,14 @@ function stringifyPlainLoop(lines, plan, topIndent = " ") {
|
|
|
14079
14230
|
template,
|
|
14080
14231
|
reactiveEffects,
|
|
14081
14232
|
childRefs,
|
|
14082
|
-
bodyIsMultiRoot
|
|
14233
|
+
bodyIsMultiRoot,
|
|
14234
|
+
anchored,
|
|
14235
|
+
anchorKeyExpr
|
|
14083
14236
|
} = plan;
|
|
14237
|
+
if (anchored) {
|
|
14238
|
+
stringifyAnchoredLoop(lines, plan, topIndent, anchorKeyExpr);
|
|
14239
|
+
return;
|
|
14240
|
+
}
|
|
14084
14241
|
if (reactiveEffects === null && !bodyIsMultiRoot && childRefs.length === 0) {
|
|
14085
14242
|
const unwrapInline = paramUnwrap ? `${paramUnwrap} ` : "";
|
|
14086
14243
|
const preamble = mapPreambleWrapped ? `${mapPreambleWrapped}; ` : "";
|
|
@@ -14107,6 +14264,39 @@ function stringifyPlainLoop(lines, plan, topIndent = " ") {
|
|
|
14107
14264
|
lines.push(`${bodyIndent}return __el`);
|
|
14108
14265
|
lines.push(`${topIndent}}, '${markerId}')`);
|
|
14109
14266
|
}
|
|
14267
|
+
function stringifyAnchoredLoop(lines, plan, topIndent, anchorKeyExpr) {
|
|
14268
|
+
const {
|
|
14269
|
+
containerVar,
|
|
14270
|
+
markerId,
|
|
14271
|
+
arrayExpr,
|
|
14272
|
+
keyFn,
|
|
14273
|
+
paramHead,
|
|
14274
|
+
paramUnwrap,
|
|
14275
|
+
indexParam,
|
|
14276
|
+
mapPreambleWrapped,
|
|
14277
|
+
reactiveEffects
|
|
14278
|
+
} = plan;
|
|
14279
|
+
const condSlot = reactiveEffects?.conditionals[0]?.slotId ?? null;
|
|
14280
|
+
lines.push(`${topIndent}mapArrayAnchored(() => ${arrayExpr}, ${containerVar}, ${keyFn}, (${paramHead}, ${indexParam}, __existing) => {`);
|
|
14281
|
+
const bodyIndent = topIndent + " ";
|
|
14282
|
+
if (paramUnwrap) lines.push(`${bodyIndent}${paramUnwrap}`);
|
|
14283
|
+
if (mapPreambleWrapped) lines.push(`${bodyIndent}${mapPreambleWrapped}`);
|
|
14284
|
+
lines.push(`${bodyIndent}const __anchor = __existing ?? document.createComment(\`bf-loop-i:\${${anchorKeyExpr}}\`)`);
|
|
14285
|
+
lines.push(`${bodyIndent}let __frag = null`);
|
|
14286
|
+
lines.push(`${bodyIndent}if (!__existing) {`);
|
|
14287
|
+
lines.push(`${bodyIndent} __frag = document.createDocumentFragment()`);
|
|
14288
|
+
lines.push(`${bodyIndent} __frag.appendChild(__anchor)`);
|
|
14289
|
+
if (condSlot) {
|
|
14290
|
+
lines.push(`${bodyIndent} __frag.appendChild(document.createComment('bf-cond-start:${condSlot}'))`);
|
|
14291
|
+
lines.push(`${bodyIndent} __frag.appendChild(document.createComment('bf-cond-end:${condSlot}'))`);
|
|
14292
|
+
}
|
|
14293
|
+
lines.push(`${bodyIndent}}`);
|
|
14294
|
+
if (reactiveEffects !== null) {
|
|
14295
|
+
stringifyReactiveEffects(lines, reactiveEffects, { indent: bodyIndent, elVar: "__anchor", bodyIsMultiRoot: false });
|
|
14296
|
+
}
|
|
14297
|
+
lines.push(`${bodyIndent}return __frag ?? __anchor`);
|
|
14298
|
+
lines.push(`${topIndent}}, '${markerId}')`);
|
|
14299
|
+
}
|
|
14110
14300
|
function stringifyStaticLoop(lines, plan) {
|
|
14111
14301
|
const { containerVar, arrayExpr, param, indexParam, childIndexExpr, attrsBySlot, texts, childRefs, csrMaterialize } = plan;
|
|
14112
14302
|
const hasAttrs = attrsBySlot.length > 0;
|
|
@@ -14667,10 +14857,10 @@ function emitArmBody2(lines, body, mode, indent) {
|
|
|
14667
14857
|
}
|
|
14668
14858
|
for (const te of body.textEffects) {
|
|
14669
14859
|
const v = varSlotId(te.slotId);
|
|
14670
|
-
lines.push(`${indent}
|
|
14860
|
+
lines.push(`${indent}let __anchor_${v} = $t(__branchScope, '${te.slotId}')[0]`);
|
|
14671
14861
|
lines.push(`${indent}__disposers.push(createDisposableEffect(() => {`);
|
|
14672
14862
|
lines.push(`${indent} const __val = ${te.expression}`);
|
|
14673
|
-
lines.push(`${indent}
|
|
14863
|
+
lines.push(`${indent} __anchor_${v} = __bfText(__anchor_${v}, __val)`);
|
|
14674
14864
|
lines.push(`${indent}}))`);
|
|
14675
14865
|
}
|
|
14676
14866
|
if (body.loops.length > 0) {
|
|
@@ -14767,6 +14957,9 @@ var init_build_component_loop = __esm({
|
|
|
14767
14957
|
|
|
14768
14958
|
// ../jsx/src/ir-to-client-js/control-flow/plan/build-loop.ts
|
|
14769
14959
|
function buildLoopPlan(elem, opts) {
|
|
14960
|
+
if (elem.bodyIsItemConditional) {
|
|
14961
|
+
return buildPlainLoopPlan(elem);
|
|
14962
|
+
}
|
|
14770
14963
|
if (elem.isStaticArray) {
|
|
14771
14964
|
return buildStaticLoopPlan(elem, opts.unsafeLocalNames);
|
|
14772
14965
|
}
|
|
@@ -14796,7 +14989,15 @@ function buildPlainLoopPlan(elem) {
|
|
|
14796
14989
|
template: elem.template,
|
|
14797
14990
|
reactiveEffects: hasReactive2 ? buildLoopReactiveEffectsPlan(elem) : null,
|
|
14798
14991
|
childRefs: buildChildRefBindings(elem.bindings.refs, elem.param, elem.paramBindings),
|
|
14799
|
-
bodyIsMultiRoot: elem.bodyIsMultiRoot ?? false
|
|
14992
|
+
bodyIsMultiRoot: elem.bodyIsMultiRoot ?? false,
|
|
14993
|
+
anchored: elem.bodyIsItemConditional ?? false,
|
|
14994
|
+
// Fall back to the iteration index when the loop has no key. A whole-item
|
|
14995
|
+
// conditional without a key is a BF023 error, but the emitted client JS
|
|
14996
|
+
// must still parse — an empty `anchorKeyExpr` would produce
|
|
14997
|
+
// `createComment(`bf-loop-i:${}`)` (a SyntaxError that breaks the whole
|
|
14998
|
+
// bundle). `elem.index || '__idx'` matches `indexParam` above, so the
|
|
14999
|
+
// anchor value stays consistent with the renderItem's own index param.
|
|
15000
|
+
anchorKeyExpr: elem.key ? wrap(elem.key) : elem.index || "__idx"
|
|
14800
15001
|
};
|
|
14801
15002
|
}
|
|
14802
15003
|
function buildStaticLoopPlan(elem, unsafeLocalNames) {
|
|
@@ -15888,17 +16089,23 @@ function runSinglePass(source, filePath, startingCounter) {
|
|
|
15888
16089
|
}
|
|
15889
16090
|
function visit3(node) {
|
|
15890
16091
|
if (ts15.isJsxAttribute(node) && node.initializer && ts15.isJsxExpression(node.initializer) && node.initializer.expression) {
|
|
15891
|
-
|
|
15892
|
-
|
|
15893
|
-
if (ts15.isArrowFunction(expr) && arrowBodyContainsJsx(expr)) {
|
|
15894
|
-
const handled = handleInlineArrow(expr);
|
|
15895
|
-
if (handled) {
|
|
15896
|
-
return;
|
|
15897
|
-
}
|
|
16092
|
+
if (tryHandleArrowValue(node.initializer.expression)) {
|
|
16093
|
+
return;
|
|
15898
16094
|
}
|
|
15899
16095
|
}
|
|
16096
|
+
if (ts15.isPropertyAssignment(node) && node.initializer) {
|
|
16097
|
+
if (tryHandleArrowValue(node.initializer)) return;
|
|
16098
|
+
}
|
|
15900
16099
|
ts15.forEachChild(node, visit3);
|
|
15901
16100
|
}
|
|
16101
|
+
function tryHandleArrowValue(initializer) {
|
|
16102
|
+
let expr = initializer;
|
|
16103
|
+
while (ts15.isParenthesizedExpression(expr)) expr = expr.expression;
|
|
16104
|
+
if (ts15.isArrowFunction(expr) && arrowBodyContainsJsx(expr)) {
|
|
16105
|
+
return handleInlineArrow(expr);
|
|
16106
|
+
}
|
|
16107
|
+
return false;
|
|
16108
|
+
}
|
|
15902
16109
|
function handleInlineArrow(arrow) {
|
|
15903
16110
|
const paramNames = collectArrowParamNames(arrow);
|
|
15904
16111
|
const free = collectFreeIdentifiers(arrow);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@barefootjs/cli",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"description": "CLI for agent-driven UI component discovery and scaffolding",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"typescript": "^5.0.0"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
|
-
"@barefootjs/jsx": "0.5.
|
|
34
|
+
"@barefootjs/jsx": "0.5.2",
|
|
35
35
|
"@types/node": "^22.0.0"
|
|
36
36
|
}
|
|
37
37
|
}
|